{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "*This notebook contains course material from [CBE30338](https://jckantor.github.io/CBE30338)\n", "by Jeffrey Kantor (jeff at nd.edu); the content is available [on Github](https://github.com/jckantor/CBE30338.git).\n", "The text is released under the [CC-BY-NC-ND-4.0 license](https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode),\n", "and code is released under the [MIT license](https://opensource.org/licenses/MIT).*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "< [Blending Tank Simulation](http://nbviewer.jupyter.org/github/jckantor/CBE30338/blob/master/notebooks/02.03-Blending-Tank-Simulation.ipynb) | [Contents](toc.ipynb) | [Hare and Lynx Population Dynamics](http://nbviewer.jupyter.org/github/jckantor/CBE30338/blob/master/notebooks/02.05-Hare-and-Lynx-Population-Dynamics.ipynb) >
"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Continuous Product Blending\n",
"\n",
"This case study is modeled after an actual process used to prepare an over-the-counter pediatric medication. The situation has been simplified to the dissolution of a single active ingredient into a suspension, and adapted to continouous rather than batch operation.\n",
"\n",
"The process objective is to mix an active ingredient $A$ into a suspension $S$, providing enough time to fully mix and dissolve components coming from both streams. The feed concentration of $A$ is 200 grams/liter. Process requirements include:\n",
"\n",
"* The final product concentration must be in the range 7.8 to 8.2 grams/liter. \n",
"* At all timses the mixer residence time, $\\frac{V}{q_{out}}$, must remain between 84 and 108 hours.\n",
"* The volume cannot exceed the tank capacity of 15,000 liters, and cannot fall below the 8,000 liters required to fully cover the mixing blades.\n",
"\n",
"The outlet from the tank is fed directly to a packaging line. The flow demand can vary due to changes in speed of the packaging equipment.\n",
"\n",
"![](figures/Continuous-Mixed-Tank.png)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Preliminary Control Analysis "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Parameters\n",
"\n",
"| Quantity | Symbol | Value | Units |\n",
"|:-:|:-:|:-:|:-:|\n",
"| Feed Concentration |$c_{A,f}$ | 200 | g/liter |\n",
"| Maximum Tank Operating Capacity | $V$ | 15,000 | liters |\n",
"| Minimum Tank Operating Capacity | $V$ | 8,000 | liters |"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Control Objectives\n",
"\n",
"* Maintain outlet concentration of $A$ at $8.0 \\pm 0.2$ grams/liter.\n",
"* Maintain an average residence time (i.e., ratio $V/q_{out}$) of $96 \\pm 12$ hours.\n",
"* Past operation has shown a liquid volume of 12,000 liters generally meets nominal demand. Demand, however, fluctuates and is determined by the downstream packaging line."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Classification of Variables\n",
"\n",
"What are the disturbances variables (DV), manipulated variables (MV), and controlled variables (CV)?\n",
"\n",
"| Variable | Symbol | Classification |\n",
"|:-:|:-:|:-:|:-:|\n",
"| Outlet Flow | $q_{out}$ | DV |\n",
"| Feed Flow | $q_A$ | MV |\n",
"| Water Makeup Flow | $q_W$ | MV |\n",
"| Product Concentration | $c_A$ | CV |\n",
"| Volume | $V$ | CV |\n",
"\n",
"In this case, we can assume the controlled variables are directly measureable."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Degrees of Freedom Analysis\n",
"\n",
"At steady-state the balance equations become algebraic equations\n",
"\n",
"\\begin{align}\n",
"0 & = \\bar{q}_A + \\bar{q}_S - \\bar{q}_{out} \\\\\n",
"& \\\\\n",
"0 & = \\bar{q}_A c_{A,f} - \\bar{q}_{out}\\bar{c}_A\n",
"\\end{align}\n",
"\n",
"There are a total of five variables in these two equations. So we need to find at least three more specifciations or constraints to determine values for these variables. \n",
"\n",
"Additional pieces of information we have are the process specifications\n",
"\n",
"\\begin{align}\n",
"\\frac{\\bar{q}_{out}}{\\bar{V}} & = 96 \\mbox{ hours} \\\\\n",
"c_{A,f} & = 200 \\mbox{ g/liter} \\\\\n",
"\\bar{q}_{out} & = \\mbox{ set by the downstream demand} \\\\\n",
"\\bar{c}_A & = 8 \\mbox{ g/liter}\n",
"\\end{align}\n",
"\n",
"This is a total of four new equations for steady-state. The new equations introduced an additional variable $\\bar{V}$. This provides six variables in six equations provided we know the downstream demand."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Steady State\n",
"\n",
"Let's assume a constant output demand $\\bar{q}_{out}$ = 125 liters/hr. The equations may then be solved in order:\n",
"\n",
"\\begin{align}\n",
"c_{A,f} & = 200 \\mbox{ g/liter} \\\\\n",
"\\bar{c}_A & = 8 \\mbox{ g/liter} \\\\\n",
"\\bar{V} & = 96\\ \\bar{q}_{out} = 12,000 \\mbox{ liters}\\\\\n",
"\\bar{q}_A & = \\frac{\\bar{q}_{out}\\bar{c}_A}{c_{A,f}} = 5 \\mbox{ liters/hr}\\\\\n",
"\\bar{q}_S & = \\bar{q}_{out} - \\bar{q}_A = 120 \\mbox{ liters/hr}\n",
"\\end{align}"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": [
"exercise"
]
},
"source": [
"### Exercise 1. Determine Operating Range\n",
"\n",
"Solve the steady-state equations for other values of $\\bar{q}_{out}$. Given a maximum tank operating capacity of 15,000 liters, what is the largest possible value of $\\bar{q}_{out}$ which still meets process specifications? If the minimum tank operating capacity is 8,000 liters, what is the minimum value of $\\bar{q}_{out}$?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Modeling\n",
"\n",
"The balance equations are\n",
"\n",
"\\begin{align}\n",
"\\frac{dV}{dt} & = q_A + q_S - q_{out} \\\\\n",
"\\frac{d(Vc_A)}{dt} & = q_A c_{A,f} - q_{out}c_A\n",
"\\end{align}\n",
"\n",
"where we have made the assumption of constant density among all of the streams, and of complete and uniform mixing in the stirred tank. \n",
"\n",
"For simulation, it is useful to isolate the derivatives of $V$ and $c_A$ on the left-hand side of these differential equations. Using the chain rule\n",
"\n",
"\\begin{align}\n",
"\\frac{dV}{dt} & = q_A + q_S - q_{out} \\\\\n",
"V\\frac{dc_A}{dt} + c_A\\frac{dV}{dt} & = q_A c_{A,f} - q_{out}c_A\n",
"\\end{align}\n",
"\n",
"Substituting the first equation into the second gives\n",
"\n",
"\\begin{align}\n",
"\\frac{dV}{dt} & = q_A + q_S - q_{out} \\\\\n",
"V\\frac{dc_A}{dt} + c_A\\left(q_A + q_S - q_{out}\\right) & = q_A c_{A,f} - q_{out}c_A\n",
"\\end{align}\n",
"\n",
"Rearranging terms, and dividing the second equation by $V$, gives us the final version of a dynamical model.\n",
"\n",
"\\begin{align}\n",
"\\frac{dV}{dt} & = q_A + q_S - q_{out} \\\\\n",
"\\frac{dc_A}{dt} & = \\frac{q_A}{V} \\left(c_{A,f} - c_A\\right) - \\frac{q_S}{V} c_A\n",
"\\end{align}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Simulation"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Step 1. Imports\n",
"\n",
"We start the simulation by importing necessary Python libraries."
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"from scipy.integrate import odeint\n",
"import matplotlib.pyplot as plt\n",
"%matplotlib inline"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Step 2. Parameter Values\n",
"\n",
"It is good coding practice to include all relevant parameter values in single block of code. This makes it easier for users of your code to understand what values are relevant to the problem, and to adjust and maintain your code. No 'magic' numbers should appear later in your code."
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [],
"source": [
"# parameters\n",
"caf = 200 # g/liter\n",
"Vmax = 15000 # liters\n",
"Vmin = 8000 # liters\n",
"\n",
"# process setpoints\n",
"ca_SP = 8 # g/liter\n",
"rtime_SP = 96 # residence time in hours\n",
"\n",
"# nominal values of the disturbance variables\n",
"qout_bar = 125"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Step 3. Computing Steady State\n",
"\n",
"A good modeling practice is to begin with the determination of a nominal steady state. By nominal we mean operating conditions representative of typical, desired behavior of the process in question, and should be computed based on the parameter values given above."
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Steady-State\n",
" V [liters] = 12000\n",
" ca [g/liter] = 8\n",
" qa [liters/hr] = 5.0\n",
" qs [liters/hr] = 120.0\n"
]
}
],
"source": [
"# nominal steady state at process setpoint\n",
"V_bar = rtime_SP * qout_bar\n",
"ca_bar = ca_SP\n",
"qa_bar = ca_SP*qout_bar/caf\n",
"qs_bar = qout_bar - qa_bar\n",
"\n",
"print(\"Steady-State\")\n",
"print(\" V [liters] = \", V_bar)\n",
"print(\" ca [g/liter] = \", ca_bar)\n",
"print(\" qa [liters/hr] = \", qa_bar)\n",
"print(\" qs [liters/hr] = \", qs_bar)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Step 4. Process Model Equations\n",
"\n",
"In our case the model consists of a system of two differential equations. For simulation with the Python function `odeint`, the model is encapsulated into a Python function that accepts two arguments.\n",
"\n",
"* `X` is a list of values for the model state, in this case `V` and `ca`.\n",
"* `t` a variable containing value corresponding to the current time."
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [],
"source": [
"def deriv(X, t):\n",
" V, ca = X\n",
" dV = qa + qs - qout\n",
" dca = qa*(caf - ca)/V - qs*ca/V\n",
" return [dV, dca]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Step 5. Numerical Solution of the Process Model\n",
"\n",
"This block does the numerically intensive portion of the simulation. Here we establish values for all degrees of freedeom, specify initial conditions, and thne proceed to compute a history of the process behavior. The history is broken into discrete time step which is a bit more work now, but will later provide a means to test implementations of control algorithms.\n",
"\n",
"For the first simulation, it usually a good idea to pick an initial condition that will yield a known result. In this case we start a simulation at steady state."
]
},
{
"cell_type": "code",
"execution_count": 44,
"metadata": {
"scrolled": false
},
"outputs": [
{
"data": {
"text/plain": [
"[[0, 12000, 8],\n",
" [1, 12000.0, 8.0],\n",
" [2, 12000.0, 8.0],\n",
" [3, 12000.0, 8.0],\n",
" [4, 12000.0, 8.0],\n",
" [5, 12000.0, 8.0],\n",
" [6, 12000.0, 8.0],\n",
" [7, 12000.0, 8.0],\n",
" [8, 12000.0, 8.0],\n",
" [9, 12000.0, 8.0]]"
]
},
"execution_count": 44,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# fix all degrees of freedom \n",
"qout = qout_bar # outlet flow -- process disturbance variable.\n",
"qa = qa_bar # inlet flow of A -- process manipulated variable\n",
"qs = qs_bar # inlet flow of suspension -- process manipulated variable\n",
"\n",
"# establish initial conditions\n",
"t = 0\n",
"V = V_bar\n",
"ca = ca_bar\n",
"\n",
"# time step, and variable to store simulation record (or history)\n",
"dt = 1\n",
"history = [[t,V,ca]]\n",
"\n",
"while t < 500:\n",
" V, ca = odeint(deriv, [V, ca], [t, t+dt])[-1]\n",
" t += dt\n",
" history.append([t,V,ca])\n",
"\n",
"history[0:10]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Step 6. Visualization and Analysis\n",
"\n",
"To simplify future simulations, here we define a function to plot the results stored in the simulation record. The function `plot_history(history, labels)` takes two arguments. The first argument is the data history recorded during the course of the simulation, where the first element is assumed to be time. The second argument is a list of labels corresponding to each of the recorded variables."
]
},
{
"cell_type": "code",
"execution_count": 45,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
" "
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"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.7.4"
}
},
"nbformat": 4,
"nbformat_minor": 2
}