diff --git a/notebooks/assignments/ProblemSet2_F23.ipynb b/notebooks/assignments/ProblemSet2_F23.ipynb index 6903a609..4de0748e 100644 --- a/notebooks/assignments/ProblemSet2_F23.ipynb +++ b/notebooks/assignments/ProblemSet2_F23.ipynb @@ -47,7 +47,9 @@ " !wget \"https://raw.githubusercontent.com/IDAES/idaes-pse/main/scripts/colab_helper.py\"\n", " import colab_helper\n", " colab_helper.install_idaes()\n", - " colab_helper.install_ipopt()" + " colab_helper.install_ipopt()\n", + "\n", + "import pyomo.environ as pyo" ] }, { @@ -593,36 +595,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## 4. Portfolio Data Analysis\n", - "\n", - "Portfolio management is a classic example of quadratic programming (optimization). The idea is to find the optimal blend of investments that achieves a specified rate of return (or better) while minimizing the variance in rate of return. In this problem, you will use your skills in statistical analysis to analyze the stock data.\n", - "\n", - "### Historical Stock Data\n", - "\n", - "Historical daily adjusted closing prices for the last five years (obtained from Yahoo! Finance) are available for the $N=5$ stocks listed in table below. (We are actually considering index funds, but this detail does not change the analysis.) \n", - "\n", - "| Symbol | Name |\n", - "|-|-|\n", - "| GSPC | S&P 500 | \n", - "| DJI | Dow Jones Industrial Average | \n", - "| IXIC | NASDAQ Composite | \n", - "| RUT | Russell 2000 |\n", - "| VIX | CBOE Volatility Index |" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 4a. Return Rate\n", - "\n", - "You are given a Stock\\_Data.csv file. Using the stock data, calculate the 1-day return rate:\n", - "\n", - "\\begin{equation}\n", - "\tr_{t,i} = \\frac{p_{t+1,i} - p_{t,i}}{p_{t,i}}\n", - "\\end{equation}\n", - "\n", - "where $p_{t+1,i}$ and $p_{t,i}$ are the *Adjusted Closing Prices* at the end of days $t+1$ and $t$, respectively, for stock $i$. These results are stored in matrix `R`. *Hint*: Use Pandas." + "## 4. Numeric Integration of Partial Differential Equations with Pyomo" ] }, { @@ -631,81 +604,117 @@ "metadata": {}, "outputs": [], "source": [ - "# This is the long path to the folder containg data files on GitHub (for the class website)\n", - "data_folder = 'https://raw.githubusercontent.com/ndcbe/data-and-computing/main/noteboohttps://raw.githubusercontent.com/ndcbe/data-and-computing/main/notebooks/data/'\n", + "%matplotlib inline\n", "\n", - "# Load the data file into Pandas\n", - "df_adj_close = pd.read_csv(data_folder + 'Stock_Data.csv')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Add your solution here" + "# Import plotting libraries\n", + "import matplotlib.pyplot as plt\n", + "from mpl_toolkits.mplot3d.axes3d import Axes3D \n", + "\n", + "# Import Pyomo\n", + "import pyomo.environ as pyo\n", + "\n", + "# Import Pyomo numeric integration features\n", + "from pyomo.dae import DerivativeVar, ContinuousSet" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### 4b. Visualization\n", + "During your time at Notre Dame, you will likely want (or at least need) to solve a partial differential equation (PDE) system. In this problem, we will practice using Pyomo to numerically integrate a simple and common PDE. (Special thanks to Prof. Kantor for this problem.)\n", "\n", - "Plot the single day return rates for the 5 stocks you obtain in the previous section and check if you obtain the following profiles:\n", + "Transport of heat in a solid is described by the familiar thermal diffusion model:\n", "\n", - "![ad](https://raw.githubusercontent.com/ndcbe/data-and-computing/main/media/stock_return_plots.png)\n", + "$$\n", + "\\begin{align*}\n", + "\\rho C_p\\frac{\\partial T}{\\partial t} & = \\nabla\\cdot(k\\nabla T)\n", + "\\end{align*}\n", + "$$\n", "\n", + "We will assume the thermal conductivity $k$ is a constant, and define thermal diffusivity in the conventional way\n", "\n", - "The first plot is made for you. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Create figure\n", - "plt.figure(figsize=(9,15))\n", + "$$\n", + "\\begin{align*}\n", + "\\alpha & = \\frac{k}{\\rho C_p}\n", + "\\end{align*}\n", + "$$\n", "\n", - "# Create subplot for DJI\n", - "plt.subplot(5,1,1)\n", - "plt.plot(R[\"DJI\"]*100,color=\"blue\",label=\"DJI\")\n", - "plt.legend(loc='best')\n", + "We will further assume symmetry with respect to all spatial coordinates except $x$ where $x$ extends from $-X$ to $+X$. The boundary conditions are\n", "\n", - "# Add your solution here\n", + "$$\n", + "\\begin{align*}\n", + "T(t,X) & = T_{\\infty} & \\forall t > 0 \\\\\n", + "\\nabla T(t,0) & = 0 & \\forall t \\geq 0 \n", + "\\end{align*}\n", + "$$\n", "\n", - "# Show plot\n", - "plt.show()" + "where we have assumed symmetry with respect to $r$ and uniform initial conditions $T(0, x) = T_0$ for all $0 \\leq r \\leq X$. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### 4c. Covariance and Correlation Matrices\n", + "### 4a. Rescaling and Dimensionless Model\n", + "\n", + "We would like a dimensionless model for two reasons: first, we only need to solve the dimensionless model once, i.e., it becomes independent of input data. Second, the dimensionless models are often scaled better for numerical solutions.\n", + "\n", + "Let's consider the following proposed scaling procedure:\n", + "\n", + "$$\n", + "\\begin{align*}\n", + "T' & = \\frac{T - T_0}{T_\\infty - T_0} \\\\\n", + "x' & = \\frac{r}{X} \\\\\n", + "t' & = t \\frac{\\alpha}{X^2}\n", + "\\end{align*}\n", + "$$\n", + "\n", + "Show this scaling procedure gives the following dimensionless system:\n", "\n", - "Write Python code to:\n", - "1. Calculate $\\bar{r}$, the average 1-day return for each stock. Store this as the variable `R_avg`.\n", - "2. Calculate $\\Sigma_{r}$, the covariance matrix of the 1-day returns. This matrix tells us how returns for each stock vary with each other (which is important because they are correlated!). Hint: pandas has a function `cov`\n", - "3. Calculate the correlation matrix for the 1-day returns. Hint: pandas has a function `corr`.\n", + "$$\n", + "\\begin{align*}\n", + "\\frac{\\partial T'}{\\partial t'} & = \\nabla^2 T'\n", + "\\end{align*}\n", + "$$\n", "\n", - "Looking at the correlation matrix, answer the follwing questions:\n", + "with auxiliary conditions\n", "\n", - "1. Which pair of stocks have the highest **positive** correlation?\n", - "2. Which pair of stocks have the highest **negative** correlation?\n", - "3. Which pair of stocks have the lowest **absolute** correlation?\n", + "$$\n", + "\\begin{align*}\n", + "T'(0, x') & = 0 & \\forall 0 \\leq x' \\leq 1\\\\\n", + "T'(t', 1) & = 1 & \\forall t' > 0\\\\\n", + "\\nabla T'(t', 0) & = 0 & \\forall t' \\geq 0 \\\\\n", + "\\end{align*}\n", + "$$\n", "\n", - "Hint: Read ahead in the class website for more information on [correlation and covariance](../..//notebooks/14/Correlation-Covariance-and-Independence.ipynb)" + "Turn in your work (pencil and paper) via **Gradescope**. *Important:* Here the prime $'$ indicates the scaled variables and coordinates. It does not indicate a derivative. Thus $T'$ is scaled temperature, NOT the derivative of temperature (which begs the question of \"with respect to what?\")." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Please write one or two sentences for each question:" + "### 4b. Numeric Integration via Pyomo\n", + "\n", + "For simplicity, let's consider planar coordinates. For a slab geometry, we want to numerical integrate the following PDE:\n", + "\n", + "$$\n", + "\\begin{align*}\n", + "\\frac{\\partial T'}{\\partial t'} & = \\frac{\\partial^2 T'}{\\partial x'^2}\n", + "\\end{align*}\n", + "$$\n", + "\n", + "with auxiliary conditions\n", + "\n", + "$$\n", + "\\begin{align*}\n", + "T'(0, x') & = 0 & \\forall 0 \\leq x' \\leq 1 \\\\\n", + "T'(t', 1) & = 1 & \\forall t' > 0\\\\\n", + "\\frac{\\partial T'}{\\partial x'} (t', 0) & = 0 & \\forall t' \\geq 0 \\\\\n", + "\\end{align*}\n", + "$$\n", + "\n", + "Complete the following Pyomo code to integrate this PDE." ] }, { @@ -714,188 +723,129 @@ "metadata": {}, "outputs": [], "source": [ - "# Add your solution here" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "nbgrader": { - "grade": true, - "grade_id": "check-R_avg", - "locked": true, - "points": "0.5", - "solution": false - } - }, - "outputs": [], - "source": [ - "# Removed autograder test. You may delete this cell." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 4c. Markowitz Mean/Variance Portfolio Model\n", + "# Create Pyomo model\n", + "m = pyo.ConcreteModel()\n", "\n", - "The Markowitz mean/variance model, shown below, computes the optimal allocation of funds in a portfolio:\n", + "# Define sets for spatial and temporal domains\n", + "m.x = ContinuousSet(bounds=(0,1))\n", + "m.t = ContinuousSet(bounds=(0,2))\n", "\n", - "\\begin{align}\n", - "\t\t\\min_{{x} \\geq {0}} \\qquad & z:= {x}^T \\cdot {\\Sigma_r} \\cdot {x} \\\\\n", - "\t\t\\text{s.t.} \\qquad & {\\bar{r}}^T \\cdot {x} \\geq \\rho \\\\\n", - "\t\t & \\sum_{i =1}^N x_i = 1 \n", - "\\end{align} \n", + "# Define scaled temperature indexed by time and space\n", + "m.T = pyo.Var(m.t, m.x)\n", "\n", + "# Define variables for the derivates\n", + "m.dTdt = DerivativeVar(m.T, wrt=m.t)\n", + "m.dTdx = DerivativeVar(m.T, wrt=m.x)\n", + "m.d2Tdx2 = DerivativeVar(m.T, wrt=(m.x, m.x))\n", "\n", - "where $x_i$ is the fraction of funds invested in stock $i$ and $x = [x_1, x_2, ..., x_N]^T$. The objective is to minimize the variance of the return rate. (As practice for the next exam, try deriving this from the error propagation formulas.) This requires the expected return rate to be at least $\\rho$. Finally, the allocation of funds must sum to 100%.\n", + "# Define PDE equation\n", + "def pde(m, t, x):\n", + " if t == 0:\n", + " return pyo.Constraint.Skip\n", + " elif x == 0 or x == 1:\n", + " return pyo.Constraint.Skip\n", + " # Add your solution here\n", "\n", - "Write Python code to solve for $\\rho = 0.08\\%.$ Report both the optimal allocation of funds and the standard deviation of the return rate. \n", - "*Hint*:\n", - "1. Be mindful of units.\n", - "2. You can solve with problem using the Pyomo function given below\n", - "3. $:=$ means ''defined as''\n", + "m.pde = pyo.Constraint(m.t, m.x, rule=pde)\n", "\n", - "Store your answer in `std_dev`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "R_avg_tolist = R_avg.values.tolist()\n", - "Cov_list = Cov.values.tolist()\n", + "# Define first auxilary condition\n", + "def initial_condition(m, x):\n", + " if x == 0 or x == 1:\n", + " return pyo.Constraint.Skip\n", + " # Add your solution here\n", "\n", - "# Optimization Problem\n", - "def create_model(rho,R_avg,Cov):\n", - " \n", - " '''\n", - " This function solves for the optimal allocation of funds in a portfolio \n", - " by minimizing the variance of the return rate\n", - " \n", - " Arguments:\n", - " rho: required portfolio expected return\n", - " Ravg: average return rates (list)\n", - " Cov: covariance matrix\n", - " \n", - " Returns:\n", - " m: Pyomo concrete model\n", - " \n", - " '''\n", - " \n", - " m = pyo.ConcreteModel()\n", - " init_x = {}\n", - " m.idx = pyo.Set(initialize=[0,1,2,3,4])\n", - " for i in m.idx:\n", - " init_x[i] = 0\n", - " m.x = pyo.Var(m.idx,initialize=init_x,bounds=(0,None))\n", - " \n", - " def Obj_func(m):\n", - " b = []\n", - " mult_result = 0\n", - " for i in m.idx:\n", - " a = 0\n", - " for j in m.idx:\n", - " a+= m.x[j]*Cov[j][i]\n", - " b.append(a)\n", - " for i in m.idx:\n", - " mult_result += b[i]*m.x[i]\n", - " \n", - " return mult_result\n", - " m.OBJ = pyo.Objective(rule=Obj_func)\n", - " \n", - " def constraint1(m):\n", - " # Add your solution here\n", + "m.ic = pyo.Constraint(m.x, rule = initial_condition)\n", "\n", - " m.C1 = pyo.Constraint(rule=constraint1)\n", - " \n", - " def constraint2(m):\n", - " # Add your solution here\n", + "# Define second auxilary condition\n", + "def boundary_condition1(m, t):\n", + " # Add your solution here\n", "\n", - " m.C2 = pyo.Constraint(rule=constraint2)\n", - " \n", - " return m\n" + "m.bc1 = pyo.Constraint(m.t, rule = boundary_condition1)\n", + "\n", + "# Define third auxilary condition\n", + "@m.Constraint(m.t)\n", + "def boundary_condition2(m, t):\n", + " # Add your solution here \n", + "\n", + "m.bc2 = pyo.Constraint(m.t, rule=boundary_condition2)\n", + "\n", + "# Define dummy objective\n", + "m.obj = pyo.Objective(expr=1)\n", + "\n", + "# Discretize spatial coordinate with forward finite difference and 50 elements\n", + "pyo.TransformationFactory('dae.finite_difference').apply_to(m, nfe=50, scheme='FORWARD', wrt=m.x)\n", + "\n", + "# Discretize time coordinate with forward finite difference and 50 elements\n", + "pyo.TransformationFactory('dae.finite_difference').apply_to(m, nfe=50, scheme='FORWARD', wrt=m.t)\n", + "pyo.SolverFactory('ipopt').solve(m, tee=True).write()" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "rho = 0.0008\n", - "model1 = create_model(rho,R_avg_tolist,Cov_list)\n", - "\n", - "#Solve Pyomo in the method learned in Class 11\n", - "\n", - "# Add your solution here" + "### 4c. Visualize Solution " ] }, { "cell_type": "code", "execution_count": null, - "metadata": { - "nbgrader": { - "grade": true, - "grade_id": "standard-deviation", - "locked": true, - "points": "0.5", - "solution": false - } - }, - "outputs": [], - "source": [ - "# Removed autograder test. You may delete this cell." - ] - }, - { - "cell_type": "markdown", "metadata": {}, + "outputs": [], "source": [ - "### 4e. Volatility and Expected Return Tradeoff\n", + "# Extract indices\n", + "x = sorted(m.x)\n", + "t = sorted(m.t)\n", "\n", - "We will now perform sensitivity analysis of the optimization problem in 3d to characterize the tradeoff between return and risk.\n", + "# Create numpy arrays to hold the solution\n", + "xgrid = np.zeros((len(t), len(x)))\n", + "# Hint: define tgrid and Tgrid the same way\n", + "# Add your solution here\n", + "\n", + "# Loop over time\n", + "for i in range(0, len(t)):\n", + " # Loop over space\n", + " for j in range(0, len(x)):\n", + " # Copy values\n", + " xgrid[i,j] = x[j]\n", + " tgrid[i,j] = t[i]\n", + " # Hint: how to access values from Pyomo variable m.T?\n", + " # Add your solution here\n", "\n", - "Write Python code to:\n", - "1. Solve the optimization problem for many values of $\\rho$ between min($\\bar{r}$) and max($\\bar{r}$) and save the results. Use the Pyomo function created in 3d.\n", - "2. Plot $\\rho$ versus $\\sqrt{z}$ (using the saved results).\n", - "3. Write at least one sentence to interpret and discuss your plot.\n", + "# Create a 3D wireframe plot of the solution\n", + "# Hint: consult the matplotlib documentation\n", + "# https://matplotlib.org/stable/gallery/mplot3d/wire3d.html\n", "\n", - "Submit your plot and discussion via **Gradescope**." + "# Add your solution here" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "rho_vals = np.arange(0.0005,0.0038,0.0001)\n", - "std_dev = []\n", + "Write a few sentences to describe the PDE solution. Is it what you expect based on your prior knowledge of this system? Each person brings different prior knwoledge to this class, you everyone should have a distinct answer. In other words, there is no \"right answer\". Instead, this is helping you practice interpreting results based on your knowledge which is a critical skill in graduate school.\n", "\n", - "# Add your solution here" + "**Discussion:**" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "#Plot\n", + "## Submission Instructions and Tips\n", "\n", - "# Add your solution here" + "1. Answer discussion questions in this notebook.\n", + "2. When asked to store a solution in a specific variable, please also print that variable.\n", + "3. Turn in this notebook via Gradescope.\n", + "4. Also turn in written (pencil and paper) work via Gradescope.\n", + "5. Even if you are not required to turn in pseudocode, you should always write pseudocode. It takes only a few minutes and can save you *hours* of time.\n", + "6. We are not using the autograder for CBE 60258, so please skip those instructions." ] }, { "cell_type": "markdown", "metadata": {}, - "source": [ - "**Discussion**:" - ] + "source": [] } ], "metadata": { diff --git a/notebooks/assignments/ProblemSet3_F23.ipynb b/notebooks/assignments/ProblemSet3_F23.ipynb index 22c5545e..c5f3bcd0 100644 --- a/notebooks/assignments/ProblemSet3_F23.ipynb +++ b/notebooks/assignments/ProblemSet3_F23.ipynb @@ -45,7 +45,16 @@ "import math\n", "import numpy as np\n", "import scipy.stats as stats\n", - "from scipy import optimize" + "from scipy import optimize\n", + "\n", + "import sys\n", + "if \"google.colab\" in sys.modules:\n", + " !wget \"https://raw.githubusercontent.com/IDAES/idaes-pse/main/scripts/colab_helper.py\"\n", + " import colab_helper\n", + " colab_helper.install_idaes()\n", + " colab_helper.install_ipopt()\n", + "\n", + "import pyomo.environ as pyo" ] }, { @@ -1088,310 +1097,363 @@ "# Removed autograder test. You may delete this cell." ] }, - { - "cell_type": "markdown", - "metadata": { - "id": "1y6sHpO5d-fR" - }, - "source": [ - "## 3. Numeric Integration of Partial Differential Equations with Pyomo" - ] - }, { "cell_type": "code", "execution_count": null, "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 357 + }, "executionInfo": { - "elapsed": 404, + "elapsed": 1668, "status": "ok", - "timestamp": 1664677364866, + "timestamp": 1664677559084, "user": { "displayName": "Alexander Dowling", "userId": "00988067626794866502" }, "user_tz": 240 }, - "id": "OyvMIfLdd-fR" + "id": "qLQGM8Urd-fT", + "outputId": "751136c4-d2eb-4edc-c183-e30b80d82128" }, "outputs": [], "source": [ - "%matplotlib inline\n", + "# Extract indices\n", + "x = sorted(m.x)\n", + "t = sorted(m.t)\n", "\n", - "# Import plotting libraries\n", - "import matplotlib.pyplot as plt\n", - "from mpl_toolkits.mplot3d.axes3d import Axes3D \n", + "# Create numpy arrays to hold the solution\n", + "xgrid = np.zeros((len(t), len(x)))\n", + "# Hint: define tgrid and Tgrid the same way\n", + "# Add your solution here\n", "\n", - "import sys\n", - "if \"google.colab\" in sys.modules:\n", - " !wget \"https://raw.githubusercontent.com/IDAES/idaes-pse/main/scripts/colab_helper.py\"\n", - " import colab_helper\n", - " colab_helper.install_idaes()\n", - " colab_helper.install_ipopt()\n", + "# Loop over time\n", + "for i in range(0, len(t)):\n", + " # Loop over space\n", + " for j in range(0, len(x)):\n", + " # Copy values\n", + " xgrid[i,j] = x[j]\n", + " tgrid[i,j] = t[i]\n", + " # Hint: how to access values from Pyomo variable m.T?\n", + " # Add your solution here\n", "\n", - "# Import Pyomo\n", - "import pyomo.environ as pyo\n", + "# Create a 3D wireframe plot of the solution\n", + "# Hint: consult the matplotlib documentation\n", + "# https://matplotlib.org/stable/gallery/mplot3d/wire3d.html\n", "\n", - "# Import Pyomo numeric integration features\n", - "from pyomo.dae import DerivativeVar, ContinuousSet" + "# Add your solution here" ] }, { "cell_type": "markdown", - "metadata": { - "id": "_GdDQNuud-fR" - }, + "metadata": {}, "source": [ - "During your time at Notre Dame, you will likely want (or at least need) to solve a partial differential equation (PDE) system. In this problem, we will practice using Pyomo to numerically integrate a simple and common PDE. (Special thanks to Prof. Kantor for this problem.)\n", + "## 3. Portfolio Data Analysis\n", "\n", - "Transport of heat in a solid is described by the familiar thermal diffusion model:\n", + "Portfolio management is a classic example of quadratic programming (optimization). The idea is to find the optimal blend of investments that achieves a specified rate of return (or better) while minimizing the variance in rate of return. In this problem, you will use your skills in statistical analysis to analyze the stock data.\n", "\n", - "$$\n", - "\\begin{align*}\n", - "\\rho C_p\\frac{\\partial T}{\\partial t} & = \\nabla\\cdot(k\\nabla T)\n", - "\\end{align*}\n", - "$$\n", + "### Historical Stock Data\n", "\n", - "We will assume the thermal conductivity $k$ is a constant, and define thermal diffusivity in the conventional way\n", + "Historical daily adjusted closing prices for the last five years (obtained from Yahoo! Finance) are available for the $N=5$ stocks listed in table below. (We are actually considering index funds, but this detail does not change the analysis.) \n", "\n", - "$$\n", - "\\begin{align*}\n", - "\\alpha & = \\frac{k}{\\rho C_p}\n", - "\\end{align*}\n", - "$$\n", - "\n", - "We will further assume symmetry with respect to all spatial coordinates except $x$ where $x$ extends from $-X$ to $+X$. The boundary conditions are\n", - "\n", - "$$\n", - "\\begin{align*}\n", - "T(t,X) & = T_{\\infty} & \\forall t > 0 \\\\\n", - "\\nabla T(t,0) & = 0 & \\forall t \\geq 0 \n", - "\\end{align*}\n", - "$$\n", - "\n", - "where we have assumed symmetry with respect to $r$ and uniform initial conditions $T(0, x) = T_0$ for all $0 \\leq r \\leq X$. " + "| Symbol | Name |\n", + "|-|-|\n", + "| GSPC | S&P 500 | \n", + "| DJI | Dow Jones Industrial Average | \n", + "| IXIC | NASDAQ Composite | \n", + "| RUT | Russell 2000 |\n", + "| VIX | CBOE Volatility Index |" ] }, { "cell_type": "markdown", - "metadata": { - "id": "S9E0AZB2d-fR" - }, + "metadata": {}, "source": [ - "### 3a. Rescaling and Dimensionless Model\n", + "### 3a. Return Rate\n", "\n", - "We would like a dimensionless model for two reasons: first, we only need to solve the dimensionless model once, i.e., it becomes independent of input data. Second, the dimensionless models are often scaled better for numerical solutions.\n", + "You are given a Stock\\_Data.csv file. Using the stock data, calculate the 1-day return rate:\n", "\n", - "Let's consider the following proposed scaling procedure:\n", - "\n", - "$$\n", - "\\begin{align*}\n", - "T' & = \\frac{T - T_0}{T_\\infty - T_0} \\\\\n", - "x' & = \\frac{r}{X} \\\\\n", - "t' & = t \\frac{\\alpha}{X^2}\n", - "\\end{align*}\n", - "$$\n", - "\n", - "Show this scaling procedure gives the following dimensionless system:\n", - "\n", - "$$\n", - "\\begin{align*}\n", - "\\frac{\\partial T'}{\\partial t'} & = \\nabla^2 T'\n", - "\\end{align*}\n", - "$$\n", - "\n", - "with auxiliary conditions\n", + "\\begin{equation}\n", + "\tr_{t,i} = \\frac{p_{t+1,i} - p_{t,i}}{p_{t,i}}\n", + "\\end{equation}\n", "\n", - "$$\n", - "\\begin{align*}\n", - "T'(0, x') & = 0 & \\forall 0 \\leq x' \\leq 1\\\\\n", - "T'(t', 1) & = 1 & \\forall t' > 0\\\\\n", - "\\nabla T'(t', 0) & = 0 & \\forall t' \\geq 0 \\\\\n", - "\\end{align*}\n", - "$$\n", + "where $p_{t+1,i}$ and $p_{t,i}$ are the *Adjusted Closing Prices* at the end of days $t+1$ and $t$, respectively, for stock $i$. These results are stored in matrix `R`. *Hint*: Use Pandas." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# This is the long path to the folder containg data files on GitHub (for the class website)\n", + "data_folder = 'https://raw.githubusercontent.com/ndcbe/data-and-computing/main/noteboohttps://raw.githubusercontent.com/ndcbe/data-and-computing/main/notebooks/data/'\n", "\n", - "Turn in your work (pencil and paper) via **Gradescope**. *Important:* Here the prime $'$ indicates the scaled variables and coordinates. It does not indicate a derivative. Thus $T'$ is scaled temperature, NOT the derivative of temperature (which begs the question of \"with respect to what?\")." + "# Load the data file into Pandas\n", + "df_adj_close = pd.read_csv(data_folder + 'Stock_Data.csv')" ] }, { - "cell_type": "markdown", - "metadata": { - "id": "vY4nyAGid-fS" - }, + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ - "### 3b. Numeric Integration via Pyomo" + "# Add your solution here" ] }, { "cell_type": "markdown", - "metadata": { - "id": "y3qGQ6Had-fS" - }, + "metadata": {}, "source": [ - "For simplicity, let's consider planar coordinates. For a slab geometry, we want to numerical integrate the following PDE:\n", + "### 3b. Visualization\n", "\n", - "$$\n", - "\\begin{align*}\n", - "\\frac{\\partial T'}{\\partial t'} & = \\frac{\\partial^2 T'}{\\partial x'^2}\n", - "\\end{align*}\n", - "$$\n", + "Plot the single day return rates for the 5 stocks you obtain in the previous section and check if you obtain the following profiles:\n", "\n", - "with auxiliary conditions\n", + "![ad](https://raw.githubusercontent.com/ndcbe/data-and-computing/main/media/stock_return_plots.png)\n", "\n", - "$$\n", - "\\begin{align*}\n", - "T'(0, x') & = 0 & \\forall 0 \\leq x' \\leq 1 \\\\\n", - "T'(t', 1) & = 1 & \\forall t' > 0\\\\\n", - "\\frac{\\partial T'}{\\partial x'} (t', 0) & = 0 & \\forall t' \\geq 0 \\\\\n", - "\\end{align*}\n", - "$$\n", "\n", - "Complete the following Pyomo code to integrate this PDE." + "The first plot is made for you. " ] }, { "cell_type": "code", "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "executionInfo": { - "elapsed": 2497, - "status": "ok", - "timestamp": 1664677371426, - "user": { - "displayName": "Alexander Dowling", - "userId": "00988067626794866502" - }, - "user_tz": 240 - }, - "id": "yM3uKVfGd-fS", - "outputId": "4150d75e-328b-4ce3-a77e-7951f7c2a89d" - }, + "metadata": {}, "outputs": [], "source": [ - "# Create Pyomo model\n", - "m = pyo.ConcreteModel()\n", + "# Create figure\n", + "plt.figure(figsize=(9,15))\n", "\n", - "# Define sets for spatial and temporal domains\n", - "m.x = ContinuousSet(bounds=(0,1))\n", - "m.t = ContinuousSet(bounds=(0,2))\n", + "# Create subplot for DJI\n", + "plt.subplot(5,1,1)\n", + "plt.plot(R[\"DJI\"]*100,color=\"blue\",label=\"DJI\")\n", + "plt.legend(loc='best')\n", "\n", - "# Define scaled temperature indexed by time and space\n", - "m.T = pyo.Var(m.t, m.x)\n", + "# Add your solution here\n", "\n", - "# Define variables for the derivates\n", - "m.dTdt = DerivativeVar(m.T, wrt=m.t)\n", - "m.dTdx = DerivativeVar(m.T, wrt=m.x)\n", - "m.d2Tdx2 = DerivativeVar(m.T, wrt=(m.x, m.x))\n", + "# Show plot\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 3c. Covariance and Correlation Matrices\n", "\n", - "# Define PDE equation\n", - "def pde(m, t, x):\n", - " if t == 0:\n", - " return pyo.Constraint.Skip\n", - " elif x == 0 or x == 1:\n", - " return pyo.Constraint.Skip\n", - " # Add your solution here\n", + "Write Python code to:\n", + "1. Calculate $\\bar{r}$, the average 1-day return for each stock. Store this as the variable `R_avg`.\n", + "2. Calculate $\\Sigma_{r}$, the covariance matrix of the 1-day returns. This matrix tells us how returns for each stock vary with each other (which is important because they are correlated!). Hint: pandas has a function `cov`\n", + "3. Calculate the correlation matrix for the 1-day returns. Hint: pandas has a function `corr`.\n", "\n", - "m.pde = pyo.Constraint(m.t, m.x, rule=pde)\n", + "Looking at the correlation matrix, answer the follwing questions:\n", "\n", - "# Define first auxilary condition\n", - "def initial_condition(m, x):\n", - " if x == 0 or x == 1:\n", - " return pyo.Constraint.Skip\n", - " # Add your solution here\n", + "1. Which pair of stocks have the highest **positive** correlation?\n", + "2. Which pair of stocks have the highest **negative** correlation?\n", + "3. Which pair of stocks have the lowest **absolute** correlation?\n", "\n", - "m.ic = pyo.Constraint(m.x, rule = initial_condition)\n", + "Hint: Read ahead in the class website for more information on [correlation and covariance](../..//notebooks/14/Correlation-Covariance-and-Independence.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Please write one or two sentences for each of the above questions:\n", + "1. Fill in here\n", + "2. Fill in here\n", + "3. Fill in here" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Add your solution here" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Removed autograder test. You may delete this cell." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 3d. Markowitz Mean/Variance Portfolio Model\n", "\n", - "# Define second auxilary condition\n", - "def boundary_condition1(m, t):\n", - " # Add your solution here\n", + "The Markowitz mean/variance model, shown below, computes the optimal allocation of funds in a portfolio:\n", "\n", - "m.bc1 = pyo.Constraint(m.t, rule = boundary_condition1)\n", + "\\begin{align}\n", + "\t\t\\min_{{x} \\geq {0}} \\qquad & z:= {x}^T \\cdot {\\Sigma_r} \\cdot {x} \\\\\n", + "\t\t\\text{s.t.} \\qquad & {\\bar{r}}^T \\cdot {x} \\geq \\rho \\\\\n", + "\t\t & \\sum_{i =1}^N x_i = 1 \n", + "\\end{align} \n", "\n", - "# Define third auxilary condition\n", - "@m.Constraint(m.t)\n", - "def boundary_condition2(m, t):\n", - " # Add your solution here \n", "\n", - "m.bc2 = pyo.Constraint(m.t, rule=boundary_condition2)\n", + "where $x_i$ is the fraction of funds invested in stock $i$ and $x = [x_1, x_2, ..., x_N]^T$. The objective is to minimize the variance of the return rate. (As practice for the next exam, try deriving this from the error propagation formulas.) This requires the expected return rate to be at least $\\rho$. Finally, the allocation of funds must sum to 100%.\n", "\n", - "# Define dummy objective\n", - "m.obj = pyo.Objective(expr=1)\n", + "Write Python code to solve for $\\rho = 0.08\\%.$ Report both the optimal allocation of funds and the standard deviation of the return rate. \n", + "*Hint*:\n", + "1. Be mindful of units.\n", + "2. You can solve with problem using the Pyomo function given below\n", + "3. $:=$ means ''defined as''\n", "\n", - "# Discretize spatial coordinate with forward finite difference and 50 elements\n", - "pyo.TransformationFactory('dae.finite_difference').apply_to(m, nfe=50, scheme='FORWARD', wrt=m.x)\n", + "Store your answer in `std_dev`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "R_avg_tolist = R_avg.values.tolist()\n", + "Cov_list = Cov.values.tolist()\n", "\n", - "# Discretize time coordinate with forward finite difference and 50 elements\n", - "pyo.TransformationFactory('dae.finite_difference').apply_to(m, nfe=50, scheme='FORWARD', wrt=m.t)\n", - "pyo.SolverFactory('ipopt').solve(m, tee=True).write()" + "# Optimization Problem\n", + "def create_model(rho,R_avg,Cov):\n", + " \n", + " '''\n", + " This function solves for the optimal allocation of funds in a portfolio \n", + " by minimizing the variance of the return rate\n", + " \n", + " Arguments:\n", + " rho: required portfolio expected return\n", + " Ravg: average return rates (list)\n", + " Cov: covariance matrix\n", + " \n", + " Returns:\n", + " m: Pyomo concrete model\n", + " \n", + " '''\n", + " \n", + " m = pyo.ConcreteModel()\n", + " init_x = {}\n", + " m.idx = pyo.Set(initialize=[0,1,2,3,4])\n", + " for i in m.idx:\n", + " init_x[i] = 0\n", + " m.x = pyo.Var(m.idx,initialize=init_x,bounds=(0,None))\n", + " \n", + " def Obj_func(m):\n", + " b = []\n", + " mult_result = 0\n", + " for i in m.idx:\n", + " a = 0\n", + " for j in m.idx:\n", + " a+= m.x[j]*Cov[j][i]\n", + " b.append(a)\n", + " for i in m.idx:\n", + " mult_result += b[i]*m.x[i]\n", + " \n", + " return mult_result\n", + " m.OBJ = pyo.Objective(rule=Obj_func)\n", + " \n", + " def constraint1(m):\n", + " # Add your solution here\n", + "\n", + " m.C1 = pyo.Constraint(rule=constraint1)\n", + " \n", + " def constraint2(m):\n", + " # Add your solution here\n", + "\n", + " m.C2 = pyo.Constraint(rule=constraint2)\n", + " \n", + " return m" ] }, { - "cell_type": "markdown", - "metadata": { - "id": "ZuOBPYd5d-fT" - }, + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ - "### 3c. Visualize Solution " + "rho = 0.0008\n", + "model1 = create_model(rho,R_avg_tolist,Cov_list)\n", + "\n", + "#Solve Pyomo in the method learned in Class 11\n", + "\n", + "# Add your solution here" ] }, { "cell_type": "code", "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 357 - }, - "executionInfo": { - "elapsed": 1668, - "status": "ok", - "timestamp": 1664677559084, - "user": { - "displayName": "Alexander Dowling", - "userId": "00988067626794866502" - }, - "user_tz": 240 - }, - "id": "qLQGM8Urd-fT", - "outputId": "751136c4-d2eb-4edc-c183-e30b80d82128" - }, + "metadata": {}, "outputs": [], "source": [ - "# Extract indices\n", - "x = sorted(m.x)\n", - "t = sorted(m.t)\n", + "# Removed autograder test. You may delete this cell." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 3e. Volatility and Expected Return Tradeoff\n", "\n", - "# Create numpy arrays to hold the solution\n", - "xgrid = np.zeros((len(t), len(x)))\n", - "# Hint: define tgrid and Tgrid the same way\n", - "# Add your solution here\n", + "We will now perform sensitivity analysis of the optimization problem in 3d to characterize the tradeoff between return and risk.\n", "\n", - "# Loop over time\n", - "for i in range(0, len(t)):\n", - " # Loop over space\n", - " for j in range(0, len(x)):\n", - " # Copy values\n", - " xgrid[i,j] = x[j]\n", - " tgrid[i,j] = t[i]\n", - " # Hint: how to access values from Pyomo variable m.T?\n", - " # Add your solution here\n", + "Write Python code to:\n", + "1. Solve the optimization problem for many values of $\\rho$ between min($\\bar{r}$) and max($\\bar{r}$) and save the results. Use the Pyomo function created in 3d.\n", + "2. Plot $\\rho$ versus $\\sqrt{z}$ (using the saved results).\n", + "3. Write at least one sentence to interpret and discuss your plot." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rho_vals = np.arange(0.0005,0.0038,0.0001)\n", + "std_dev = []\n", "\n", - "# Create a 3D wireframe plot of the solution\n", - "# Hint: consult the matplotlib documentation\n", - "# https://matplotlib.org/stable/gallery/mplot3d/wire3d.html\n", + "# Add your solution here" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#Plot\n", "\n", "# Add your solution here" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Discussion**:" + ] + }, { "cell_type": "markdown", "metadata": { "id": "X1GR0jYZd-e9" }, "source": [ - "**Submission Instructions and Tips:**\n", + "## Submission Instructions and Tips\n", + "\n", "1. Answer discussion questions in this notebook.\n", "2. When asked to store a solution in a specific variable, please also print that variable.\n", "3. Turn in this notebook via Gradescope.\n", @@ -1399,6 +1461,11 @@ "5. Even if you are not required to turn in pseudocode, you should always write pseudocode. It takes only a few minutes and can save you *hours* of time.\n", "6. For this assignment especially, read the problem statements twice. They contain important information and tips that are easy to miss on the first read." ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] } ], "metadata": { diff --git a/notebooks/assignments/ProblemSet4_F23.ipynb b/notebooks/assignments/ProblemSet4_F23.ipynb new file mode 100644 index 00000000..f2330c0c --- /dev/null +++ b/notebooks/assignments/ProblemSet4_F23.ipynb @@ -0,0 +1,321 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Problem Set 4\n", + "\n", + "CBE 60258, University of Notre Dame. © Prof. Alexander Dowling, 2023\n", + "\n", + "You may not distribution the solutions without written permissions from Prof. Alexander Dowling.\n", + "\n", + "**Your Name and Email:**" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Import Libraries" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import math\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1. Measuring Acceleration Two Ways\n", + "\n", + "You and a classmate want to measure the acceleration of a cart rolling down an incline plane, but disagree on the best approach. The cart starts at rest and travels distance $l = 1.0$ m. The location of the finish line is measured with negligible uncertainty. You (student 1) measure the instantaneous velocity $v = 3.2 \\pm 0.1 $ m/s at the finish line. Your classmate (student 2) instead measures the elapsed time $t = 0.63 \\pm 0.01$s." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 1a. Approach 1\n", + "\n", + "Calculate the acceleration for approach 1,\n", + "\\begin{equation}\n", + "\ta_1 = \\frac{v^2}{2l} ~,\n", + "\\end{equation}\n", + "\n", + "and estimate the associated uncertainty. Round your answer to the correct number of significant digits and store your answers in variables `A1` for acceleration and `U_A1` for the uncertainty." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Add your solution here" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "nbgrader": { + "grade": true, + "grade_id": "acceleration-a", + "locked": true, + "points": "0.4", + "solution": false + } + }, + "outputs": [], + "source": [ + "# Removed autograder test. You may delete this cell." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 1b. Approach 2\n", + "\n", + "Calculate the acceleration for approach 2,\n", + "\\begin{equation}\n", + "\ta_2 = \\frac{2 l}{t^2}~,\n", + "\\end{equation}\n", + "\n", + "and estimate the associated uncertainty. Round your answer to the correct number of significant digits and store your answers in variables `A2` for acceleration and `U_A2` for the uncertainty." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Add your solution here" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "nbgrader": { + "grade": true, + "grade_id": "acceleration-b", + "locked": true, + "points": "0.4", + "solution": false + } + }, + "outputs": [], + "source": [ + "# Removed autograder test. You may delete this cell." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 1c. Weighted Average\n", + "\n", + "A third classmate suggests to use a weighted average of your two calculations:\n", + "\n", + "$$\n", + "\ta_{3} = w a_1 + (1-w) a_2\n", + "$$\n", + "\n", + "where $0 \\leq w \\leq 1$ is the weight you place on the approach 1 calculation calculations. Determine the value of $w$ that minimizes the uncertainty in $a_3$. Do the following steps: \n", + "1. Make a plot to graphically determine this value of $w$ and from the plot, read the minimum value for $w$ and save it as the variable `weight`. Submit your plot via **Gradescope**.\n", + "2. Then calculate the acceleration and uncertainty from the above equation. Round your answer for acceleration and corresponding uncertainty to the correct number of significant digits and store your answers in variables `A3` for acceleration and `U_A3` for the uncertainty. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Add your solution here\n", + "\n", + "print(\"weight =\",weight)\n", + "print(\"A3 =\",A3)\n", + "print(\"U_A3 =\",U_A3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "nbgrader": { + "grade": true, + "grade_id": "acceleration-c", + "locked": true, + "points": "0.3", + "solution": false + } + }, + "outputs": [], + "source": [ + "# Removed autograder test. You may delete this cell." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 1d. Analysis\n", + "\n", + "Write one or two sentences (each) to answer the following questions:\n", + "\n", + "1. If restricted to use only $a_1$ or $a_2$, which would you choose? Why?\n", + "2. How can a weighted average reduce the uncertainty? Why does this make sense?\n", + "3. Why does the uncertainty in $a_3$ depend on $w$?\n", + "\n", + "Record your answers below." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Q1:\n", + " \n", + "Q2:\n", + " \n", + "Q3:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2. Calorimetry for Food Analysis\n", + "\n", + "As an intern at Tasty Foods, Inc., you are asked to estimate the caloric content (kilo-calories per gram) of mayo: You burn a $0.40 \\pm 0.01$ gram sample of mayo in a calorimeter and measure a 2.75 $\\pm$ 0.02 $^\\circ{}$C temperature increase. You then calculate caloric content $C$:\n", + "\n", + "\\begin{equation}\n", + "\tC = \\frac{c ~ H ~ \\Delta T}{m} \n", + "\\end{equation}\n", + "\n", + "where $c = 0.2390$ kcal/kJ is a conversion factor. Assume the calorimeter heat capacity $H = 4.0$ kJ/$^\\circ{}$ C is known with negligible uncertainty." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Add your solution here" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Removed autograder test. You may delete this cell." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 2b. Relative Uncertainty\n", + "\n", + "Find the relative uncertainty in $C$ by doing the following:\n", + "\n", + "1. Set $\\sigma_m = 0.01 m$ and $\\sigma_{\\Delta T} = 0$ and recalculate $\\sigma_C$. This tells us the impact of 1% uncertainty in $m$. Store your answer as variable `U_C_mass`.\n", + "2. Set $\\sigma_m = 0$ and $\\sigma_{\\Delta T} = 0.01 \\Delta T$ and recalculate $\\sigma_C$. This tells us the impact of 1% uncertainty in $\\Delta T$. Store your answer as variable `U_C_temperature`.\n", + "\n", + "*Hint*: Use the $m$ and $\\Delta T$ data reported above.\n", + "\n", + "Remember to store your answer using the correct number of significant digits." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Add your solution here" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Removed autograder test. You may delete this cell." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 2c. Which is better?\n", + "\n", + "Which would provide a greater reduction in $\\sigma_C$: i) reducing the uncertainty in $m$ to 0.005 g OR ii) reducing the uncertainty in $\\Delta T$ to 0.001 $^\\circ{}$C? Do the following steps:\n", + "1. Calculate the uncertainty for each scenario, storing your variables as i) `Reduce_mass` and ii) `Reduce_temperature`. \n", + "2. After determining which method would provide a greater reduction in $\\sigma_C$, set the variable `method` equal to either 1, for reducing the uncertainty in mass, or 2, for reducing the uncertainty in temperature, to save which method you found would more significantly reduce $\\sigma_C$.\n", + "\n", + "Remember to use the correct number of significant digits." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Add your solution here" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Removed autograder test. You may delete this cell." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.12" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/notebooks/contrib-dev/Filtration.ipynb b/notebooks/contrib-dev/Filtration.ipynb index 6cf775fb..956baae5 100644 --- a/notebooks/contrib-dev/Filtration.ipynb +++ b/notebooks/contrib-dev/Filtration.ipynb @@ -1,1030 +1,1314 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "id": "bAbiPnfCLETk" - }, - "source": [ - "# Filtration of a Yeast Suspension\n", - "\n", - "**Prepared by**: Hailey Lynch (hlynch@nd.edu) and Wilson Raney (wraney@nd.edu)\n", - "\n", - "**Reference**: Example 11.1 in Bioprocess Engineering: Basic Concepts, 3rd Edition by Shuler, Kargi, and DeLisa (ISBN-10: 0137062702) (2017)\n", - "\n", - "> This problem will illustrate a filtration of a yeast suspension example for undergraduates to practice solving analyical solutions, developing Python code, and discussing critical thinking questions using concepts from CBE 60258.\n", - "\n", - "## Learning Objectives\n", - "After completing this notebook and working through through the activities, you should be able to:\n", - "\n", - "* Write and solve systems of differential equations and quadratic functions on paper and use Python for a simple separations example in chemical engineering.\n", - "* Develop a mathematical model on paper and implement in Python to calculate solutions using numerical methods.\n", - "* Interpret results and discuss critical thinking questions.\n", - "\n", - "**Specific CBE 60258 notebooks utilized throughout this notebook:**\n", - "* (Section 1.4) Systems of Differential Equations: https://ndcbe.github.io/data-and-computing/notebooks/07/Systems-of-Differential-Equations-and-Scipy.html#systems-of-differential-equations\n", - "\n", - "* (Section 1.5) Visualization with matplotlib: https://ndcbe.github.io/data-and-computing/notebooks/01/Matplotlib.html#customizing-plots\n", - "\n", - "\n", - "* (Section 1.5) Visualizing Data: https://ndcbe.github.io/data-and-computing/notebooks/09/Visualizing-Data.html#scatter-plots\n", - "\n", - "* (Section 1.8) Newton-Raphson Method in One Dimension: https://ndcbe.github.io/data-and-computing/notebooks/06/Newton-Raphson-Method-in-One-Dimension.html\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "5zKXuMWnvhNS" - }, - "outputs": [], - "source": [ - "# import libraries\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "from matplotlib.pyplot import figure\n", - "import math" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Niaqr4eUv1zT" - }, - "source": [ - "## Problem Statement\n", - "The following data were obtained in a constant-pressure filtration unit for filtration of a yeast suspension:\n", - "\n", - "t (min) | V (L filtrate) \n", - "-------------------|------------------\n", - "4 | 115 \n", - "20 | 365 \n", - "48 | 680\n", - "76 | 850 \n", - "120 | 1130 \n", - "\n", - "\t\t\n", - "Characteristics of the filter are as follows:
\n", - "$A = 0.28 m^2$, $C = 1920 kg/m^3$, $\\mu = 2.9ᐧ10^{-3} kg/mᐧs$, $\\alpha = 4 m/kg$ \n", - "\n", - "1. Determine the pressure drop across the filter.\n", - "2. Determine the filter medium resistance ($r_{m}$).\n", - "3. Determine the size of the filter for the same pressure drop to process 4000 L of cell suspension in 20 minutes.\n", - "\n", - "**Do the following throughout this notebook:**\n", - "1. Propose a mathematical model for the given problem statement.\n", - "2. Solve the problem analytically.\n", - "3. Write pseudocode before using Python.\n", - "4. Solve the problem using Python.\n", - "5. Print answers to the screen.\n", - "6. Answer discussion questions." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "EK7yingcviST" - }, - "source": [ - "## Governing Equations for the Filtration System\n", - "\n", - "Consider the following for separation of constant-pressure filtration:\n", - "\n", - "For filtration, the **rate** (*the flow of filtrate*) for a **constant-pressure** (*vacuum*) filtration operation is determined by the **resistance of the cake** and the **filter medium**. \n", - "\n", - "We can understand this by setting up a differential equation to model this relationship:\n", - "\n", - "$$Equation (11.1):\\frac{dV}{dt} = \\frac{g_{c}ΔPA}{(r_{m}+r_{c})𝜇}$$\n", - "\n", - "where:\n", - "\n", - "\n", - "$V$ = the volume of filtrate\n", - "\n", - "$A$ = the surface area of the filter\n", - "\n", - "$ΔP$ = the pressure-drop through the cake and filter medium\n", - "\n", - "$𝜇$ = the viscosity of the filtrate\n", - "\n", - "$r_{m}$ = the resistance of the filter medium\n", - "\n", - "$r_{c}$ = the resistance of the cake\n", - "\n", - "\n", - "---\n", - "\n", - "The value of $r_{m}$ is characterisitc of the filter medium. However the cake resistance $r_{c}$, increases during filtration and after a start-up period, $r_{c}$ exceeds $r_{m}$. The value of $r_{c}$ is given by:\n", - "\n", - "$$ Equation (11.2): r_{c} = 𝛼\\frac{W}{A} = 𝛼\\frac{CV}{A} $$\n", - "\n", - "where:\n", - "\n", - "$W$ = the total weight of the cake on filter\n", - "\n", - "$C$ = the weight of the cake deposited per unit volume of filtrate\n", - "\n", - "$𝛼$ = the average specific resistance of the cake\n", - "\n", - "---\n", - "\n", - "The total weight of the cake is related to the total volume of filtrate and can be characterized by:\n", - "

\n", - "$$Equation (11.3): W = CV$$\n", - "\n", - "where:\n", - "\n", - "$W$ = the total weight of the cake on filter\n", - "\n", - "$C$ = the weight of the cake deposited per unit volume of filtrate\n", - "\n", - "$V$ = the volume of filtrate\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "z4mx6zRK5HA7" - }, - "source": [ - "## Describe the Filtration System Analytically\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "WER8ry9DQukM" - }, - "source": [ - "With the information given in 1.3, we can now use these equations to define the mathematical relationship to solve the problem.\n", - "\n", - "Use the previous three equations (11.1), (11.2), and (11.3) to analytically derive the Ruth equation:

$$ Equation (11.6): \\frac{t}{V} = \\frac{1}{K}(V+2V_{0})$$
\n", - "where $V_{0}$ and $K$, the Ruth coefficient, are defined as:

\n", - "$V_{0} = \\frac{r_{m}}{𝛼C}A$ and $K = (\\frac{2A^{2}}{𝛼C𝜇})Δpg_{c}$
\n", - "\n", - " Record your answer using pencil and paper. \n", - "\n", - ">**Hint**: You will need to solve for two equations (11.4) and (11.5) before deriving the Ruth equation.
\n", - "
How can you transform a differential equation into a linear equation?
What bounds of $V$ and $t$ will you need to integrate within?" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Qzyt4ObhfqWl" - }, - "source": [ - "**Paper and Pencil Solution:**

\n", - "Substituting Equations (11.2) and (11.3) into Equation (11.1) we get:\n", - "

\n", - "$$Equation (11.4):\n", - "\\frac{d(V/A)}{dt} = \\frac{g_{c}Δp}{(r_{m}+𝛼\\frac{CV}{A})𝜇} $$\n", - "
\n", - "\n", - "Integrating Equation 11.4 from $V=0$ to $V=V$ and $t=0$ and $t=t$ gives:\n", - "

\n", - "$$Equation (11.5):\n", - "V^{2}+2VV_{0}=Kt$$\n", - "\n", - "where $V_{0}$ and $K$ are defined as:\n", - "\n", - "$$V_{0} = \\frac{r_{m}}{𝛼C}A$$\n", - "\n", - "and\n", - "\n", - "$$K = (\\frac{2A^{2}}{𝛼C𝜇})Δpg_{c}$$\n", - "\n", - "For a constant-pressure filtration, 11.5 is known as the Ruth equation and we can rearrange it to get:\n", - "

\n", - "$$Equation (11.6):\n", - "\\frac{t}{V} = \\frac{1}{K}(V+2V_{0})$$" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Mtdg1Tk0oJqp" - }, - "source": [ - "The integration performed here could also be modeled in Python as outlined in this notebook: (https://ndcbe.github.io/data-and-computing/notebooks/07/Systems-of-Differential-Equations-and-Scipy.html#systems-of-differential-equations)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "eXUpJKfQ6cDo" - }, - "source": [ - "Additional information on these and similar properties of constant-pressure filtration can be found here: (https://pubs.acs.org/doi/pdf/10.1021/ie50306a024)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "O05WUdtJAi9n" - }, - "source": [ - "## Determine Pressure Drop Across the Filter\n", - "\n", - "\n", - "In order to determine the pressure drop across the filter, we have to apply the Ruth equation (11.6) to the data from the Problem Statement in 1.2 to get the new data.\n", - "\n", - "Linearize the Ruth equation into the form $y=mx+b$.\n", - "\n", - "**Record your answer using paper and pencil.**" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "icJdQD7tk6yv" - }, - "source": [ - "**Pencil and Paper Solution**:\n", - "\n", - "$$Equation (11.6):\n", - "\\frac{t}{V} = \\frac{1}{K}(V+2V_{0})$$\n", - "\n", - "Multiply out terms and group into linear form:\n", - "\n", - "$$\\frac{t}{V} = \\frac{V}{K}+ \\frac{2V_{0}}{K}$$\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "1SF8irq3muVf" - }, - "source": [ - "Now we will use this linearized form to calculate our new data table.\n", - "
\n", - "**Record your answer using paper and pencil.**\n", - "\n", - "**Paper and Pencil Solution:**\n", - "
\n", - "Plug in the values from the data in Problem Statement 1.2 into the linearized form of Equation 11.6:\n", - "\n", - "For example: $t=4$ and $V=115$, \n", - "\n", - "$\\frac{4}{115} = 0.0348 = 0.035$ \\\\\n", - "\n", - "$\\Rightarrow$ The rest of the values for $t$ and $V$ can be substituted into this equation as shown above to get the transformed data below:\n", - "\n", - "V (L) | t/V (min/L) \n", - "-------------------|------------------\n", - "115 | 0.035\n", - "365 | 0.055\n", - "680 | 0.071\n", - "850 | 0.089\n", - "1130 | 0.11 \n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "kJ55uR4gQzsD" - }, - "source": [ - "Now, we will plot the Ruth equation ( $V$ vs. $t/V$ ) which can be used to determine the Ruth coefficient $K$.\n", - "\n", - "> **Hint**: When the Ruth Equation in the form $y=mx+b$, what is the slope of the line? What is the y-intercept?" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "x41vq0ZtQ0pp" - }, - "source": [ - "In Python, you will need to:\n", - "1. Store the data for $V$ and $t$ in a list and calculate $t/V$.\n", - "2. Write pseudocode to perform this operation.\n", - "3. Store the appropriate data in the lists called `t`, `V` and `t_over_V`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "bAbiPnfCLETk" + }, + "source": [ + "# Filtration of a Yeast Suspension\n", + "\n", + "**Prepared by**: Hailey Lynch (hlynch@nd.edu) and Wilson Raney (wraney@nd.edu)\n", + "\n", + "**Edited by**: Emmanuel Barias (ebarias@nd.edu)\n", + "\n", + "**Reference**: Example 11.1 in Bioprocess Engineering: Basic Concepts, 3rd Edition by Shuler, Kargi, and DeLisa (ISBN-10: 0137062702) (2017)\n", + "\n", + "> This problem will illustrate the filtration of a yeast suspension. This example is meant for undergraduates to practice solving analyical solutions, developing Python code, and discussing critical thinking questions using concepts from CBE 60258.\n", + "\n", + "# Learning Objectives\n", + "After completing this notebook and working through through the activities, you should be able to:\n", + "\n", + "* Write and solve systems of differential equations and quadratic functions on paper and use Python for a simple separations example in chemical engineering.\n", + "* Develop a mathematical model on paper and implement in Python to calculate solutions using numerical methods.\n", + "* Interpret results and discuss critical thinking questions.\n", + "\n", + "# Sources\n", + "**Specific CBE 60258 notebooks utilized throughout this notebook:**\n", + "* (Section 1.4) Systems of Differential Equations: https://ndcbe.github.io/data-and-computing/notebooks/07/Systems-of-Differential-Equations-and-Scipy.html#systems-of-differential-equations\n", + "\n", + "* (Section 1.5) Visualization with matplotlib: https://ndcbe.github.io/data-and-computing/notebooks/01/Matplotlib.html#customizing-plots\n", + "\n", + "\n", + "* (Section 1.5) Visualizing Data: https://ndcbe.github.io/data-and-computing/notebooks/09/Visualizing-Data.html#scatter-plots\n", + "\n", + "* (Section 1.8) Newton-Raphson Method in One Dimension: https://ndcbe.github.io/data-and-computing/notebooks/06/Newton-Raphson-Method-in-One-Dimension.html\n", + "\n" + ] }, - "id": "ckuAUutV5Ebf", - "outputId": "40c32018-f2f2-41f7-bab1-af34f0cc986a" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "The values of t/V are: [0.03478261 0.05479452 0.07058824 0.08941176 0.10619469]\n" - ] - } - ], - "source": [ - "# Calculating t/V\n", - "\n", - "# import data\n", - "### BEGIN SOLUTION\n", - "t = [4, 20, 48, 76, 120] # min\n", - "V = [115, 365, 680, 850, 1130] # L\n", - "### END SOLUTION\n", - "\n", - "# calculate t/V\n", - "### BEGIN SOLUTION\n", - "t_over_V = []\n", - "for i in range(len(t)):\n", - " t_over_V = np.append(t_over_V, t[i] / V[i])\n", - "### END SOLUTION\n", - "print(\"The values of t/V are:\", t_over_V)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "g1B7ca3CQ9IH" - }, - "source": [ - "Finish the code below to generate a scatter plot of $V$ vs. $t/V$. Give the axes appropriate titles.
\n", - "> **Hint**: matplotlib.pyplot has the built-in scatterplot tool `plt.scatter()`.\n", - "
\n", - "\n", - "* Additional information on matplotlib can be found here: https://ndcbe.github.io/data-and-computing/notebooks/01/Matplotlib.html#customizing-plots\n", - "* Additional information on plt.scatter() can be found here: https://ndcbe.github.io/data-and-computing/notebooks/09/Visualizing-Data.html#scatter-plots" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 1000 + "cell_type": "markdown", + "source": [ + "# Import Libraries" + ], + "metadata": { + "id": "Lrkmt5LSntDc" + } }, - "id": "sfLTzPVfQ96y", - "outputId": "5b245bfc-fc13-49e1-b4ec-6bf35e5cd0ed" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "The equation of the linear trendline is: y=0.000070x+0.027163\n" - ] + "cell_type": "code", + "execution_count": 3, + "metadata": { + "id": "5zKXuMWnvhNS" + }, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "from matplotlib.pyplot import figure\n", + "import math" + ] }, { - "data": { - "image/png": "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A2DhOu645NWvTmoddSUAAMAEsaUUgIXprW9NXv/6zv9eujQ59tjR1gMAAEwMV7gBsLDUmhx99J/CtiR585sFbgAAwNC4wg2AhaPW5HWvS97+9rv2HXVUst56yWteM/y6AACAieIKNwAWhlqTV76ye9h2pyOPTC65ZHg1AQAAE8kVbgDMf6tWJUcckXzwg81jSkk+8IFkl12GVxcAADCRBG4AzG8rVyYvelHy8Y83j1m0KPnoR5PnPW94dQEAABNL4AbA/LViRfLc5yaf+lTzmMWLkxNOSA49dHh1AQAAE03gBsD8tHx58sxnJp/7XPOYddZJTjopeepTh1cXAAAw8QRuAMw/t92WHHJIcvrpzWPWWy/57GeTJz1peHUBAABE4AbAfHPLLclBByVnn908Zv31k1NPTfbdd3h1AQAATBG4ATB//PGPyQEHJBde2Dxmo406V7497nHDqwsAAGA1AjcA5odly5InPjH55jebx2yySXLmmcljHjO8ugAAANYicANg/F1/fbLffsnFFzeP2WyzzjbT3XYbXl0AAABdCNwAGG/XXtu5F9sllzSP2XLL5Jxzkl12GV5dAAAADQRuAIyva65J9tkn+clPmsdsvXVy7rnJTjsNry4AAIBpCNwAGF+//nVy9dXN/dtsk5x3XrLDDsOrCQAAYAaLRl0AADTabbfkjDOSDTe8a99223WeVipsAwAAxozADYDx9hd/kZx2WrJ06Z/adtghueCCTugGAAAwZgRuAIy/PfdMvvCFZN11kwc/uBO23ec+o64KAACgK/dwA2B+2G+/zvbSXXbpPJUUAABgTAncAJg/9tln1BUAAADMyJZSAAAAAGiRwA2A0bnwwuSww5Lly0ddCQAAQGtsKQVgNM45JznwwOTWWzuB24knJkssSwAAwPznCjcAhu+MM5IDDuiEbUny6U8nz3tesnLlaOsCAABogcANgOH63OeSgw5Kbr99zfZPfjI5/PBk1arR1AUAANASgRsAw3PSScnTn56sWNG9/6MfTT7xieHWBAAA0DKBGwDD8YlPJM961vTbRp///OQ5zxlaSQAAAHNB4AbA3PvQhzph2nTbRV/84uQjH0kWLx5aWQAAAHNB4AbA3HrvezthWq3NY17xiuT9708WWZYAAID5zycbAObOO96RvPzl04957WuT445LShlOTQAAAHNM4AbA3DjmmOTII6cfc/TRyVvfKmwDAAAWlCWjLgCABabW5Kijkje/efpxb3lL8g//MJyaAAAAhkjgBkB7ak1e/erkXe+aftxxxyWvfOVwagIAABgygRsA7Vi1KnnZy5L3vW/6ce97X/KSlwynJgAAgBEQuAEwuJUrO08i/chHmseUknz4w8kLXzi8ugAAAEZA4AbAYO64I3nBC5Ljj28es3hx8vGPJ4cdNry6AAAARkTgBkD/VqzohGinnNI8ZsmS5MQTk0MOGV5dAAAAIyRwA6B/v/99ctFFzf3rrpt8+tPJU54yvJoAAABGbNGoCwBgHtt66+S885J73/uufUuXJqeeKmwDAAAmjsANgMHc//6d0O0e9/hT2wYbJGeckey//+jqAgAAGBGBGwCD23HH5Nxzky22SDbeODnrrGSvvUZdFQAAwEi4hxsA7dh55+Scc5Lbbkse9ahRVwMAADAyAjcA2vOwh426AgAAgJGzpRQAAAAAWiRwA6DZ1Vcnhx6a3HDDqCsBAACYN2wpBaC7X/4y2Xvv5Oc/T664onN/tk02GXVVAAAAY88VbgDc1c9/njzucZ1/Jsl3vpM86UnJTTeNti4AAIB5QOAGwJp+9rNkjz2Sq65as/3rX0+e8pTklltGUxcAAMA8IXAD4E9+9KNO2Hb11d37v/KV5LWvHWpJAAAA843ADYCO738/2XPP5Le/bR7z8IcnRx89tJIAAADmI4EbAJ17tO29d3Lddc1jHvWo5Nxzky22GF5dAAAA85DADWDSff3ryT77JH/4Q/OYxz42+fKXk802G15dAAAA85TADWCSnX9+sv/+yR//2Dxm772TM89MNtlkeHUBAADMYwI3gEl11lnJk56U3Hxz85gnPCE57bRkww2HVxcAAMA8t2TUBTA6pZStkxyQZO8kD06yTZKNk6xMsizJL5L8MMlZSc6std4yolIHVkp5ZJInJ3lkkgcl2TzJBkluSXJdkp8muSjJabXW74yqzrWVUjZO8qMk951m2Ha11iuHUxELxhe/mBxySLJ8efOYAw9MTj45WW+94dUFAACwAAjcJlApZYck/5zkkHT/b2CdJEuT3CPJY5IcnuQPpZQPJnlLrfXGYdU6qFLKM5L8Y5JdGoZsPPW1bZInJPmnUsp/JXlzrfWUoRQ5vbdm+rANeveZzyTPfGZyxx3NY57+9OSEE5J11hleXQAAAAuELaUTppRyZDpXrT0zvQWud0vy2iQ/K6XsNxe1tamUslUp5ctJTkpz2NbkYUlOLqWcWUrZsv3qZqeU8hdJjhjV+VmgPvnJ5NBDpw/bnv3szjhhGwAAQF8EbhOilLK4lHJCkn9JMsj+sHsm+VIpZWyDoFLKjkm+m+TxA061f5LvTl0ROFSllPWSfDhJGfa5WcA++tFOmLZyZfOYv/mb5GMfS5a4ABoAAKBfArfJ8dEkz5phzDXpBFXfT+e+Zk0WJfn3UsoLWqqtNaWU+yQ5N8l9phm2MsnPknw7yY+TTHMTq9w3yXlT8w7TG9K51xy04/3vT170oqTW5jF/93fJBz+YLF48vLoAAAAWIIHbBCilvCzJcxu6b0vy9iQPqLXeq9a6e631z2qtW6SztfI/0gmo7jJtkveXUnabk6L7UEpZJ8kp6Tz8oZvLkjwvyaa11gfVWh9da905ySZJDk7yvYbjtkly0tT8c66U8tB0tu+u7nfDODcL1HHHJUfMcFHqq16VvPe9ySLLAgAAwKB8slrgSikPSPKOhu6rkjyy1vraWusv1u6stV5Saz08yT5Jbuhy/HpJPlFKGZe9Z69N8qiGvhOSPKzW+ola682rd9Rab6+1fi6dJ5g2vVePSXJka5U2KKUsTvKRdB5csbpXzvW5WaBWrEhOmeH5H//4j8k735kUO5gBAADaIHBb+I5L93u2XZdkr1rrD2eaoNZ6QTpP8Ly9S/eDk7x0oApbUErZJsnrG7o/k+S5tdZbp5uj1rqy1npkknc3DHl9KeXeA5Q5G69IsvtabWfVWk+c4/OyUK2zTnLGGcluDRejHnNMcuyxwjYAAIAWCdwWsFLKw5M8paH7xbXWK2Y7V631onTuK9bNa6du8j9Kr0myfpf2Xyd5Ya11VY9z/aBL+wZJXt1HbbNSStkuyTFrNd+S5CVzdU4mxN3ulpx1VrLLWg/sfcc7kjc0/d8aAACAfgncFra/b2i/sNb6mT7me0+Su2w9TefJpTOganY3AAAgAElEQVQ9kGHOlFI2SfKihu431Fpv7GW+WusdSf5PQ/f/V0rZuJf5evChdEK91f1zL8EoNNp88+Tss5Oddup8/973Jq+es/wYAABgogncFqhSykZJntbQ/fZ+5qy1rkjzdsumhzIMw9OSbNil/Zokx/czYa31vHR/iMKG6TxgoVVTT3zdd63mHyb517bPxQTbaqvk3HOTE05IXjryneAAAAALlsBt4Togd71aKkl+m+TMAeb9ZJIVXdr3KKXcc4B5B3FoQ/snp0LCfn28x/P1Zep9WztYW5Xk8Kmr7aA9W2+dPGtkF6QCAABMBIHbwrVfQ/sXa60r+5201npdkq926SpJHt/vvP2aunfc4xq6Pzfg9E3H79HyPev+Pclma7W9v9b6rRbPAQAAAAyJwG3h2ruh/ZwW5m6ao+mcc+kx6f6whJuSfHuQiWut/5Pk0i5dGyR59CBz36mU8tTcdevvr9P8xFVY0/LlyUteklx++agrAQAAYIrAbQEqpWyR5H4N3Re1cIqmOXZrYe5ePaKh/Xstbcecs9daSrlbkv/bpetlvT7ogQl1223JwQcnH/hAsvfeyZVXjroiAAAAInBbqB7e0H5jS0+8/H5D+04tb7WcjabX+l8tzd/0WndtYe53JNl6rbZTa62fbWFuFrpbbkkOPDA5/fTO9//zP53Q7Ve/Gm1dAAAACNwWqB0a2i9rY/Ja6/VJbujStSTJdm2cowdNr7XbVtB+NO3T23GQSUspeyX5m7Wab0ri0ZHM7Kabkic9KTn77DXbr7iiE7pdc81o6gIAACCJwG2hagq9rmzxHE1zDTtwm+vX2jRP36+zlLJ+kg916Tpq6r5x0GzZsmT//ZMLLujef9llySGHJLUOty4AAAD+l8BtYdqmof03LZ6jaa6mc7duavvqFg3dbb3Wpnm2KqWs2+ecb0qy/VptFyd5b5/zMSmuvz7Zd9/kG99oHrPZZsm//VtSyvDqAgAAYA0Ct4Vp84b237d4jmt7PPdcmO5cbb3W6ea5e6+TlVJ2S/KqtZpXJjm81rqy1/mYINde29ku+t3vNo/Zcsvk/POT3Ubx/BIAAADutGTUBTAnNmtob/PJl3/s8dxzYbpztfJaa62rSik3J9mw4fyzvpKulLIkyYeTLF6r6z211u/1X+XwXH550y3tmm255ZbZaqut5qCaCfKb3yT77JP8+MfNY7beOjnnnOTBDx5eXQAAAHQlcFuYNmhov6XFc9zc47nnwnTnavu1dgvcen2tr8ldn256VZJ/6qeoUTjooIN6Puboo4/OG9/4xvaLmRS/+lUnbLt0mueAbLNNct55yQ5NzxABAABgmGwpXZia7i12R4vnaJqr3/ua9WO6c43Vay2l7Jjuwdrf1Vqbwksm3ZVXJo973PRh23bbJRdeKGwDAAAYIwK3hanpysVhhFDrtHiOmTReoVlrHZvXWkop6WwlXbpW1ym11tMHKYwF7LLLOmHbFVc0j9lhh87TSrcb9sOBAQAAmI4tpQtTU0C09r3DBtH0386KFs8xk8ZQrZSyuMWHEAz6Wv82yV+u1bYsySv6rmhEPv/5z2f77dd+wOr0ttxyyzmqZgH7yU8620ivuaZ5zE47Jeee27l3GwAAAGNF4LYwLW9ob/Pfd9NcTeeeC9Oda0k6T/9sQ9+vtZSyTZK3den6h1rrNGnKeNp+++2z8847j7qMhe2SS5J99+08lbTJLrskZ5+deBgFAADAWLKldGFqemBAtxv/96tprltbPMdMpnswwri81vcn2WSttm8m+cBAFbEwXXxxstde04dtu+2WnH++sA0AAGCMCdwWphsa2tcOfgaxcUP79S2eYyZNrzNp6bWWUhalOXCb9rWWUp6Z5IC1mlckObzWWlsoj4XkW9/qbCO9fpr/rB7zmM420rvffXh1AQAA0DOB28J0XUP75i2eo+nGXE3nngvTnaut17rFNH2NyUgpZfMk/9al65211h8NXBULy4UXJo9/fLJsWfOYPfZIzjor2XTT4dUFAABAXwRuC9PVDe33bPEc9+jx3K2rtd6e5tCtrdfa9DqvrbVOdw+3V+auoeTPkxzTSlUsLB//eHLTTc39j398csYZycZNF5YCAAAwTjw0YWG6oqF92xbP0TTXL1o8x2xcke5Xs23b0vxN88z0OrslI+smObuUMlBBXZxSSrm9S/sZtda3tH0y5sAHPpDccEPyuc/dte/JT04+/elk6dLh1wUAAEBfBG4L02UN7Tu0MXkp5e5Jut1E6o40h31z5bIkj+jSvmNL8ze9Z03v8XTuM/XVtm6vP0kun4NzMRfWWSf51KeSgw9OTj/9T+1PfWqnfd11R1cbAAAAPbOldGH6fkP7pqWUbVuYf9eG9p9ObfMcpqbX+rCW5m96rT9oaX7oWHfdzpVs++7b+f6Zz0xOOknYBgAAMA8J3BagWuu1Sa5q6H5kC6domuPiFubu1Xca2v+slLK4hfnH6bWy0C1dmnzhC8nb354cf3znyjcAAADmHYHbwnV+Q/s+LczdNMd5Lczdq28lua1L+8YZMFwspWyT5IFdum5N8s1B5oZGG2yQvOY1yeI28mIAAABGQeC2cH25of3AUkrf/96n7t/2uC5dNcnZ/c7br1rrbUkubOh+6oDTH9TQfsFMW2drra+stZY2v6Y53XYNxzy/71cOAAAA9E3gtnCdls6VWGu7Z5L9B5j3r9N52ubavlprvWaAeQdxckP7s0opgzwY5Lk9ng+6O+645IILRl0FAAAAQyJwW6BqrX9M8tmG7iP7mXMqvHplQ/cn+pmzJZ9OckuX9nsnOayfCUspeybZvUvXLUk+08+cTKhjj01e9arkgAOSb9qJDAAAMAkEbgvbcQ3te5ZS+tlu+bIk23dp/22SE3qZqJTysVJK7fJ1Za9F1VqXJfloQ/expZSNe6xtSZJ/bej+cK31xl7mY0LVmrzhDclRR3W+v+mm5IlPTC72vA0AAICFTuC2gNVaL05yRkP3h0op2852rlLKI5K8taH7HVP3Uhuld6T7wxO2SfKRUsp090Bb29uS/FmX9luTvLOP2pg0tSavfnXy5jev2b5sWbLffskll4ymLgAAAIZC4LbwvTLJ8i7tWyQ5r5Sy80wTlFL+MslZSdbr0v3TJP82UIUtqLX+Ms2B4NOTfKyUsnS6OUopi0opb03yfxqGvLXW+j8DlMkkWLUqeelLk3e9q3v/9dd3QrcbXSgJAACwUAncFrha62VJXtvQvV2S75RS3lpK2W7tzlLKQ0opH0hyfpK7dzl+eZLn1VpXtFbwYN6W5DsNfc9N8l+llMNKKRus3lFKWbeU8ldJvp3kdQ3HX5TkX1qrlIVp5crk8MOT972veUwpyVvekmyyyfDqAgAAYKgGeYIj80St9d1TW0K7PUBg/XRCpteVUq5O8ut0gtj7pXMV3HReWmu9qNViB1BrXV5KeXqSbyS5V5chOyY5PskdpZTLkixLsnE696XrdvXenX6d5Bm11m5XCkLHHXckL3hBcvzxzWMWLUo+8YnksL6e5QEAAMA8IXCbHM9P59/3odOMuffU10xWJXllrfU/WqirVbXWq0op+yQ5J82vZUmSnWY55a+S7FtrvaqN+ligVqzohGinnNI8ZsmS5MQTk0MOGV5dAAAAjIQtpROi1npHkr9O8vp0v6fbbP0uyQG11ve2UtgcqLX+NMluSc4dcKpzkuxWa/3Z4FWxYN1+eydEmy5sW3fd5LOfFbYBAABMCFe4TZBaa03y1lLKZ5Ick+TgzP6/gWVJPpTkzbXWZXNUYmtqrb9Nsm8p5ZnphIwP7eHwHyZ5S631U3NSXP/e1ND+h6FWwZ/cemty8MHJmWc2j1m6NPn855P99x9eXQAAAIyUwG0C1VovTXJoKeVeSQ5MsmeSndPZgrlxkpVJbkzyiySXJDk7yRm11ptbrOH56WxznVNTodmnSimPTvLkJI9M8sAkmyfZIMktSa5L8rN0Hppweq3123NdVz9qrW8cdQ2s5uabk6c8JTn//OYxG2yQnHZastdew6sLAACAkRO4TbBa66+TfGDqa0GrtX4rybdGXQcLxI03Jk9+cvK1rzWP2Xjj5Iwzksc+dnh1AQAAMBYEbgC9uOGG5AlPSC6a5gG9d7tbctZZySMfOby6AAAAGBsCN4DZ+v3vk/32S77//eYxm2+enH128vCHD68uAAAAxorADWA2fvvbZN99kx/9qHnMPe6RnHNO8pCHDK8uAAAAxo7ADWAmV1+d7LNP8rOfNY+5172S885LHvjA4dUFAADAWFo06gIAxt6nPz192Hbf+yYXXihsAwAAIInADWBmL395cuSR3fse8IBO2PaABwy3JgAAAMaWwA1gJqUkb3tbJ3hb3YMelFxwQXK/+42mLgAAAMaSwA1gNkpJ3v3u5MUv7nz/kIckX/lKcu97j7QsAAAAxo+HJgDMVinJ+97XCdle8pJkiy1GXREAAABjSOAG0ItFi5Kjjhp1FQAAAIwxW0oBAAAAoEUCN4Dzz0+OP37UVQAAALBA2FIKTLazzkoOOihZvjxZZ53k0ENHXREAAADznCvcgMl16qnJgQcmt92WrFqVHHZY8rnPjboqAAAA5jmBGzCZPv3p5GlP61zZdqeVKztXuJ1xxujqAgAAYN4TuAGT54QTOsHaHXfctW/FiuTgg5NLLhl+XQAAACwIAjdgsnz0o8lzntPZQtrk2c9Odt55eDUBAACwoHhoAjAvLbt1RX786xtzy/IuV6mtZYN1l+TB99okm37sw8kRR0w/+Igjkve+N1nk7xEAAAD0R+AGzCsXXHptjjntx7n8dzf1dNyLvvP5HHXeh6cf9KpXJe98Z1LKABUCAAAw6QRuwLxwx8pV+adT/zuf/PYvez72iG+enCMv/MT0g17/+uTYY4VtAAAADEzgBoy9O1auyitO+kFOv+Sa3g6sNX//tU/mFd84cfpxxxyTvOEN/RcIAAAAqxG4AWNtkLDtdRd8LH/77c9MO2zl2/4li1975AAVAgAAwJrcFRwYW4OEbUef+6EZw7aj931xXn6ffXPHymmeWAoAAAA9coUbMJb6DdtKXZVjv/y+HPaDMxvHrErJP+7/dzlx1yckU/O/59Bds2Sxv0EAAAAwOIEbMHb6DdsWrVqZt3/p33LIj85tHLOyLMqRT3xFPvPQff637XShGwAAAC0SuAFjpd+wbcnKO/Ku09+VA39yYfPcZVFe+ZRX57SdHneXPqEbAAAAbfGpEhgr/3Tqf/cctq2zckX+/dR/mTZsW75oSf7uoNd1DdvudPol1+ToU/+7p3MDAADA2gRuwNj4ys9+l09++5c9H/eIX/04+136rcb+2xevk8MP/secteOfzzjXCd/+ZS649NqeawAAAIA7CdyAsXHs6T/p67hv3u9hee0TX96179Yl6+WFhxydrzxg91nPd8xpP+6rDgAAAEgEbsCYWHbLilz+u5v6Pv6UXR6fN+x3xBptN627fp73jDfl69vu2tNcl//upiy7dUXftQAAADDZBG7AWPjxNTcOPMfxD39Sjtn7b5IkN667QZ77jH/ORfd5SF9z/aSFegAAAJhMnlIKjIVblt/Ryjwf2f2g1JRcdJ+d86N7bt/3PDff3k49AAAATB6BG7DgfHT3vxp1CQAAAEwwW0oBAAAAoEUCNwAAAABokcANAAAAAFokcAMAAACAFgncAAAAAKBFAjcAAAAAaJHADQAAAABaJHADxsIG6y4ZdQlr2HC98aoHAACA+UPgBoyFB99rk1GXsIadth6vegAAAJg/BG7AWNh0/XWy/VYbjbqMJMn2W22UTddfZ9RlAAAAME8J3ICxcdQBDx51CUnGpw4AAADmJ4EbMDb22HHLPOtR9x1pDYc96r7ZY8ctR1oDAAAA85vADRgr/3zgznnyLluP5NxP3mXrvOnAnUdybgAAABYOgRswVpYsXpT3HLrr0EO3J++ydd5z6K5ZstiPRQAAAAbjkyUwdoYdugnbAAAAaJNPl8BYGlboJmwDAACgbT5hAmNrrkM3YRsAAABzwadMYKzNVegmbAMAAGCu+KQJjL07Q7fDHnXfVuY77FH3FbYBAAAwZ5aMugCA2ViyeFHe/NSHZr+d75ljTvtxLv/dTT3Psf1WG+WoAx6cPXbccg4qBAAAgA6BGzCv7LHjltnjVXtk2a0r8pNrbszNt98x4zEbrrckO229STZdf50hVAgAAMCkE7gB89Km66+TR99/81GXAQAAAHfhBkYAAAAA0CKBGwAAAAC0SOAGAAAAAC0SuAEAAABAiwRuAAAAANAigRsAAAAAtGjJqAsAAKB/v/vd7/K+973vf78/4ogjstVWW42wIgAmhTUImgncAADmsWuvvTZvetOb/vf7pz/96T7sADAU1iBoZkspAAAAALRI4AYAAAAALRK4AQAAAECLBG4AAAAA0CKBGwAAAAC0SOAGAAAAAC0SuAEAAABAiwRuAAAAANAigRsAAAAAtEjgBgAAAAAtErgBAAAAQIuWjLoAYKytu/o3l19++ajqAKDB2j+b/awGYFisQcyFLv8drdtt3LgrtdZR1wCMqVLKgUm+MOo6AAAAmFh/VWs9ddRF9MqWUgAAAABokcANAAAAAFpkSynQqJSyaZI9Vmv6nyTLR1QOAAAAC9+6Se6z2vcX1FqXjaqYfgncAAAAAKBFtpQCAAAAQIsEbgAAAADQIoEbAAAAALRI4AYAAAAALRK4AQAAAECLBG4AAAAA0CKBGwAAAAC0SOAGAAAAAC0SuAEAAABAiwRuAAAAANAigRsAAAAAtEjgBgAAAAAtWjLqAgAA6E0pZeckT0ny50l2SrJlko2S3JrkhiSXJrk4yRlJvlprXTXE2hYleVySJybZLcmOSTZLsn6Sm5Jcm+QnSb6e5Iu11h8PqzaAuVJK2SSdn3l/lmTjLkP+UGt993Crsl4MWN+GSZ6QZL8kuyTZLsmmSRYn+WOSXyX57yTnT9X3m2HWx/grtdZR1wAAMHSllD3T+SV5rlxXa92izQlLKfsmOTrJY3s47OdJ3pHkP+byg9TUB6fDk7wmyf17OPRrSd5Uaz1nTgoDaFkpZYMkuybZPckjpv65Y5IyzWFX1Vq3nfvqOqwX/SulbJrkH5K8OMndZnnYiiSfSXJUrfXyuaqN+UXgBgBMpPkUuE39lf2DSQ4bYJrvJjm01vqLNmpaXSnlAUlOSucKhX4dn+Rva603t1MVQDtKKesneV7+FLDtnM5VTr0YSuBmvRhMKeUJST6W5B59TnF7kjfUWt/ZWlHMWwI3AGAizZfArZSyVZJzkjx08JJyQ5In11q/2cJcSZJSyp8nOS2dbUCDuiTJ42utv2thLoBWlFK2TXLFgNPMeeBmvRhMKeWlSd6Tdu51f0KS59VaV7YwF/OUhyYAAIypqW0tM314qulsA7ooyQ/TuS9Pk82SfKmU8rCW6ts1yZcy/Yen29O5x823k/wsyXQfPnZJcvbUvZAAmCXrxcD1vTDJezN9RrIsyQ+SfCfJL2eY8rAkH26jNuYvD00AAFjTZUna+Iv5H1qY4yNp/vB0TZI3Jzmx1nr9nY2llMVJ9k7yuql/rm3TJJ8ppexWa13Wb2FTH+4+k6Tpw87FSY5Nckatdflqx22U5OAkb0iyQ5fjdknndT+939oAJpD1ov/6HpHk/dMMOSXJu5J8u662RbCUcq8kL0jnXnSbdjnu+aWUi2ut/z5IfcxftpQCABNpmi2lL6i1fmy41dxVKeXZSf6zofucJM+otd4wwxyvSHJcut/I+8O11v9vgPo+nORFDd3vSPIP022lmbon0n+k+T5Dz661ntBvfQBtmWFL6aokP03nvmffTecKqW5PI52zLaXWi/7Xi1LKOkn+K50nuK7t9iTPqbWeMsMc903yhXQepNFtjgfPxf3wGH8CNwBgIo1z4DZ10+vLk9yzS/c3k+xda71tlnO9It0//K1Ksnut9Xt91PeIdLYkdftgdlyt9VWznGdRkpOTPK1L96+T7FBrvaXX+gDatFrgVpNcmj+Faxcn+d7qN++fZm2Zk8DNepFkgPWilPKqJP/apasmObjW+vlZzrN5OlthH9Cl+wu11oN6rY35zz3cAADGz+Hp/uHpliTPnO2HpySptb4nyelduhYlOaq/8nJUun94+kGSI2c7Sa11VZIXpvNhaW33StL3FRUALbo2yV5JNq21PqjW+uxa67trrV8dgycrWy/6XC9KKUunqeH/zjZsm6rvuiTPSieoW9tftXUvPOYXgRsAwBgppZQkr2jofmetdaYbNXfz9+lcobC2vyqldPtrfKNSyvZJntLQ/apa6x29zFdrvTHNH+ReOfV+AIxMrfXmWutXaq1/HHUtq7NerKGf9eJZSe7RpX1Zkn/qca7UWi9KcnxD96yu5GNhEbgBAIyXPZPcr0v7bek8Qa1ntdbLknT7S31J8uwep3tOul+t8N1aa7dtVLNxfJLfdGnfNsnj+pwTYKHbM9aLO22b3teL5zW0/8dM97ybxjsa2p82tf2XCSJwAwAYL4c2tJ9ea/39APN+vMfzNWka3zT/jKaeSndij+cDmHTWi9md7y5KKVsn+cuG7kHq+2GS73fp2jDJk/udl/lJ4AYAMF72a2j/3IDznpXOPX3WtlMp5T6zmWDqSWwPbOgetL6m4/cfcF6Ahcp6saZe1ovHp/vVd5fXWn/Ue0lrsJ6RROAGADA2pp6Et11D9zmDzF1rvT3J1xq6957lNE3jflprvbr3qtbwrSTdbj5+/1JKty1TABPLejHwetFU30Dv3QxzzPa9Y4EQuAEAjI9HNLT/stb62xbmv6ihfbdZHt9UX9O8s1ZrXZHu23CS2dcHMCmsF92NvL50alvRpX3bUsrdW5ifeULgBgAwPh7e0P5fLc3f9AFl11keP+71AUyKcf95PLb1lVKWJnlQQ/fA9dVab0vy04Zu69kEWTLqAgAAxsyiUsrDkjw2yU5JtkyyRZKazj1tfp3kiiQ/SPLNWuuNLZ57h4b2S1ua//KG9h1nefy41wcwKcb95/E413f/JIsb+i7rr5y7uDzJQ7u075jkvJbOwZgTuAEArOlDaf5FfG0rSikXJPlokk9PbXMZRNP9eK4ccN6Z5rlHKWWDWmu3m2QnSUopG6YTPvYyb6+a5ml6XwAmlfWiu9msF01jrqu1/rG/cu7iyh7PzQJkSykAwJpmG7YlyTpJ9k3yySQ/LaU8fcBzb9PQ/psB502STF2Nd2tD971nOLyptqSl+qaZZ7pzA0wi60Xv555pTFu1TTeX9WyCCNwAANpx/yQnl1JOmvrrfj+abqb8+z7n6+bahvbNZziuqb8mub7/ctbQVJubTAOsyXrR3WzWi6b6xuG9YwERuAEAtOsZSb5aSunpl+qpkG7dhu427xPXtF1msxmOa+q/qda6aoB6VtdU29JSyvotnQNgXrNeJBlsvWiqbxzeOxYQ93ADAOi4IcmXk3wnyQ/TuXHysnR+Ad8wnb9Kb5vkL5Psl+TPp5nr4UnOLKU8rtbatCVnbRtM09d4r5w+3NzH+afrH0Ztd55/tu8lwEJmvRhsvRhlfTO9dywgAjcAYJLdkuSkJP8vyTdqrSsbxi2b+vpFOk8Xe1Mp5ZFJjkknfOvmEUn+b5IXzrKWpqsVkuSOWc4xG01zTXf+6fqHUdt05weYNNaLwdaLUdZnLZsgtpQCAJPqv5Lcu9b6wlrrV6cJ27qqtV6U5AlJXpPmX6xfUErZe5ZTTveH0GF8CFhnhuOa6hvWB6iZ6gOYFNaLwdaLUdZnLZsgAjcAYCLVWm+otf5hwDlqrfWdSV46zbA3z3K66X7R7+XJqTNp+qCxYobjmuobRm3JzPUBTArrxWDrxSjrs5ZNEIEbAMCAaq0fTPLBhu5Hl1J2n8U0y6fpa/M2IE1zTXf+6fqHUdt05weYNNaLwdaLUdZnLZsgAjcAgHYcleankj1jFsdPd7PmDXsvp+e5ZnogQVN9w6gt8cAEgDtZLwZbL0ZZn7VsggjcAABaUGu9NsnHG7r3msXxN6f5L9+b9FtXFxs3tF8/w3E3NLRvVEopA9Szuqbabqu1tvn0OIB5y3qRZLD1oqm+cXjvWEAEbgAA7flSQ/vDSimzuTdM0y/im/dZTzdbNLRfN8NxTf0lyd37L2cNWza0+4ACsCbrRXezWS+a6mvzvWuqb6b3jgVE4AYA0J6vN7QvSXKfWRx/dUP7PfsrZ02llI2TbNDjuWfT30p9Se7Rx7kBJpH1ovdzzzSmrdoS6xkRuAEAtKbWuizJ7Q3dTVcKrO6KhvZt+ypo9vP8dqYtOLXWm5L8vsd5e9U0zy9amh9gobBedDeb9aLpvduilLJRf+XcxbYN7dazCSJwAwBoV9N2lqWzOPayhvYd+6xlbTv0eN7ZjhuX+gAmxbj/PB7n+n6RZGWP8/bKeobADQCgZU03Sm66wfXqvt/Q/rA+a1nbrg3tP5jl8eNeH8CkGPefx2NbX6311iQ/a+geuL5SynpJHtTQbT2bIAI3AICWlFKWJmnajrJsFlN8p6H9fqWUrfqrag2PbGi/eJbHN9XXNO+slVKWJHl4Q/ds6wOYFNaL7kZeXzq1rdOl/apaq4cmTBCBGwBAe/6sob0muWqmg2utVya5sqF7n/5K6iilrJvkLxu6z5vlNOc3tO9USrlX71Wt4VHpHlZeMfW+ADDFejHwetFU30Dv3QxzzPa9Y4EQuAEAtGf/hvaf11pvm+UcX25of2of9axuv3R/4txPa62/nM0Etdarklza0H1Qv4VNaXp9Zw04L8BCZb1YUy/rxdnp/DFsbTuWUnbqvaQ1WM9IInADAGjF1D1bnt/Q/bUepjq5of2AUsrdeypqTc/r8XxNmsY3zT+jUso6Sf66x/MBTDrrxezOdxe11l8n+XpD9/N7LOt/lVJ2TrJbl65bkpze7wdi+ckAAA05SURBVLzMTwI3AIB2/F2S+zb0fa6Hec5P8j9d2tdP8rJei0qSUsoD0v0v7jXJf/Y43X+m+1UBjyyl7NFrbVMOS9Jti9Evk1zQ55wAC5314k/6WS8+3tB+eCll0x7nutNrGto/W2u9qc85macEbgAAAyql/HmSNzd0/yrJl2Y7V611VZL3NHS/upRynx7LS5J3JVncpf2LtdbLe5mo1nppmv9K/66pm1nPWill4yTHNnS/e+r9AGAt1os19LNenJDk2i7td0vyzz3OlVLKI5I8p6H7Xb3Ox/wncAMAJkopZftSyusH+Ov12vP9RZIvJFnaMORttdYVPU77wSS/7dK+UZITp7avzra+lyY5sEtXTXJMj3Xd6dh0v2rhz5K8bbaTlFJKko8kuXeX7t8k+VBf1QFMDutFn+tFrfXWJO9o6H5ZKeUpPdS3WZJPpnvGclqt9fu91sf8J3ADACbNRulcjXZVKeUtpZQH9zNJKWXDUspRSb6SZIuGYd9N8oFe557adnJkQ/dfJDm1lHK3WdT40jRf/fD/aq3f7bW2qfq+neRjDd3/p5Ty1lLKtL9nllKWprOd5+kNQ46std7cT30Ak8J6kWSw9eI9SX7W7bRJTi6lHDzTBKWUbZKck2SHLt23J/n7Pmtjniu1dgubAQAWplLKrknW/kvzT9K5Su27SX6Q5Be1yy9JpZT1kzwmnaeR/k2S6W5KfV2S3WutVwxQ6+fS/DS3X6dz5cCJtdY/rHbM4iR7Jnldkn0bjr0yycNXP66P2jZL8r0k2zYM+W46V0ScWWtdvtpxG6Zzf6CjkuzYcOzna62DPmUPoDWllIcnee8shm6a5CFd2m9P5+fijGqtj+2htCTWi35rmzrPI9N5uNE6DUNOTvKvSb6z+u8GpZSt03nAwpHpbEPt5pW11qYgkwVO4AYATJSGwG1ttya5PsmyJH9MskE64dpWaf6FfHU3JNm31vq9AUrN1FUJX08y3VV4q5L8fKre9ZNsP1VvkxuT7DVobVP17ZbOTbs3nmbY7UkuS3JTOh9Itk8y3X17/jvJYwf5cAfQtlLKnun8vJtztdbS6zHWi4HrOzyd7bnT+UOSK5KsSHKPJPebYfzxtdame7oxAQRuAMBEmWXgNoivJXlOrfXKNiYrpdwzna0qO7cw3R+SHFBr/XoLcyVJSimPTXJaOld1DOpH6QSV3e5HBDAy4x64JdaLQZVSXpHOww3auPXWp5I8u9a6soW5mKfcww0AoB1/TPKPSfZsK2xLklrrb5I8Op1f3gfxvXS2uLb24SlJaq1fS7J7Bg8xT0zyGGEbQH+sF4OZ2vr5lCS/G2Ca5Un+odb618I2BG4AwKS5NMkLk3wiyVUtzPfTJC9Pcu9a61vm4hfsWutNtda/Tufecd/o8fBfJDkiySNrrZe3XVuS1FovS+dD1BHpbLfpxTeS7FdrfdbUzb8B6JP1YjC11jOSPDCdp5cu6+HQO5KclOShtdZZP32Vhc2WUgBgopVS7pFk1yQPS+emzPdNcp8km6dzb5ul6dxX5oZ0tthcl+SHSb6Z5Fu11p+PoOaHpvNX+D9P8qB07i23wWp1Xprk4iRfSvKVWuuqIda2KJ2bcD8xyW7pvKebJVkvyc1Jrk0npPxGki/WWn84rNoAJo31YqD6Npqq7fHp/I6wXZJNkixO56r2q9O5j9z5U/X9epj1Mf4EbgAAAADQIltKAQAAAKBFAjcAAAAAaJHADQAAAABaJHADAAAAgBYJ3AAAAACgRQI3AAAAAGiRwA0AAAAAWiRwAwAAAIAWCdwAAAAAoEUCNwAAAABokcANAAAAAFokcAMAAACAFgncAAAAAKBFAjcAAAAAaJHADQAAAABaJHADAAAAgBYJ3AAAAACgRQI3AAAAAGiRwA0AAAAAWiRwAwAAAIAWCdwAAAAAoEUCNwAAAABokcANAAAAAFokcAMAAACAFgncAAAAAKBFAjcAAAAAaJHADQAAAABaJHADAIA+lFKeX0qpLX79YNSvqU2llI+1/P68cdSvCQBmS+AGAAAAAC1aMuoCAAAYjVLKk5I8sodDViZ5S6115RyVlCQppTwqyRN7POzfaq3Xz0U946iUsmeSPXs5ptb6xrmoBQC4K4EbAMDkWifJ0T0e8+0kX56DWlb3hiQH9DD+FxMYJu2Z3v/dvbH9MgCAbmwpBQCYXGckua7HY541F4XcqZRy9yT793jY8XNRCwBAvwRuAAATqta6IslJPR721FLK0rmoZ8rT07nyrhcCNwBgrAjcAAAm23/2OH6T9Lbds1e9XkH3rVrrZXNSCQBAnwRuAAATrNb6rSSX93jYYXNRSyllmyR/2eNhvQaGo/LxWmuZ4WvXURfZplrr82d6zUneNOo6AWAuCNwAAOg1tHpiKeVuc1DHXycpPYzvZ0ssAMCcE7gBANDrPdDWS/K0Oaij1+2kZ9Rae33oAwDAnBO4AQBMuFrrL5J8vcfDWn1aaSnlQUl63VI5X7aTAgATRuAGAEDSe3i1ZynlXi2ev9f7wv0hyWktnh8AoDUCNwAAkuTkJMt7GL8oyTNbPH+vV8ydXGu9vcXzAwC0RuAGAEBqrTek9yvGWtlWWkp5dJL793iY7aQAwNgSuAEAcKdeQ6zdSik7tnDeXoO7K9L7PecAAIZG4AYAwJ3OSHJ9j8cMdJVbKWVxkmf0eNjxtdY6yHkBAOaSwA0AgCRJrXV5kpN6PGzQbaX7JLlHj8ccP+A5AQDmlMANAIDV9bqtdIdSyu4DnK/XwO7btdZLBzgfAMCcE7gBAPC/aq3fTHJ5j4f1dZVbKWVpkqf2eJiHJQAAY0/gBgDA2nrdsnloKaWf3ysPSLJJD+NXpPctrwAAQydwAwBgbb0Gblsn2auP8xzW4/gv1Vp/38d5AACGSuAGAMAaaq0/T/KNHg/rKTwrpdwtyRN7PIftpADAvCBwAwCgm17DrYNLKev1MP5pSXoZ/4ckX+ytJACA0RC4AQDQzclJlvcwftMkT+5hfK8PWjil1np7j8cAAIyEwA0AgLuotV6f5PQeD5tViFZK2TrJnj3ObTspADBvCNwAAGjSa8j15FLKbJ46+sz09nvolUm+1mMtAAAjI3ADAKDJ6Umu72H80nTuzTaTXreTHl9rrT0eAwAwMgI3AAC6qrUuT+debr2YNkwrpeyQ5BE9zmk7KQAwrwjcAACYTq9h116llHtO09/r1W0X1Vov7fEYAICRErgBANCo1vqNJD/v4ZDFSQ6dpr/XwM3VbQDAvCNwAwBgJsf3OP6wbo2llEck2bGHeVYk+VSP5wYAGDmBGwAAM+k1cNu9lLJ9l/Zer247s9b6+x6PAQAYOYEbAADTqrVenuSbPR62RrhWSlmU6beadmM7KQAwLwncAACYjV7Dr7WvZtszyb16OH5Zki/2eE4AgLEgcAMAYDZOSrK8h/EPLKXsttr3vW4nPaXWeluPxwAAjAWBGwAAM6q1Xp/kjB4Pe1aSlFLWTfK0Ho+1nRQAmLcEbgAAzFavIdihU/due1KSu/Vw3FVJvtrjuQAAxobADQCA2To9yQ09jL93kj3S+3bS42uttcdjAADGhsANAIBZqbXenuTkHg/72yRP6fEY20kBgHlN4AYAQC96DcOekWRpD+O/U2v9WY/nAAAYKwI3AABmrdb69SS/mMNTuLoNAJj3BG4AAPTq+Dma944kn5qjuQEAhkbgBgBAr+YqcDuz1nrtHM098UopdS6+Rv26AGAcCdwAAOhJrfWyJN+ag6ltJwUAFgSBGwAA/Wg7HFuW5NSW5wQAGAmBGwAA/TgpyYoW5/t0rfW2FucDABgZgRsAAD2rtV6X5IwWp7SdFABYMARuAAD0q62Q7KokF7Y0FwDAyAncAADo12lJ/tDCPCfUWj3tEgBYMARuAAD0pdZ6e5KTW5jKdlIAYEERuAEAMIhBw7Lv1lp/2kolAABjQuAGAMAgvp7kigGOd3UbALDgLBl1AQAAzF9T9167/6jrmDS11jcmeeOIywAAGrjCDQAA5t7zSil1hq8fjLrINpVSPjbTa05y9KjrBIC5IHADAAAAgBYJ3AAAAACgRQI3AAAAAGiRwA0AAAAAWiRwAwAAAIAWCdwAAAAAoEWl1jrqGgAAAABgwXCFGwAAAAC0SOAGAAAAAC0SuAEAAABAiwRuAAAAANAigRsAAAAAtEjgBgAAAAAtErgBAAAAQIsEbgAAAADQIoEbAAAAALRI4AYAAAAALRK4AQAAAECLBG4AAAAA0CKBGwAAAAC0SOAGAAAAAC0SuAEAAABAiwRuAAAAANAigRsAAAAAtEjgBgAAAAAtErgBAAAAQIsEbgAAAADQIoEbAAAAALRI4AYAAAAALRK4AQAAAECLBG4AAAAA0CKBGwAAAAC0SOAGAAAAAC0SuAEAAABAiwRuAAAAANAigRsAAAAAtEjgBgAAAAAtErgBAAAAQIsEbgAAAADQIoEbAAAAALRI4AYAAADw/7djxwIAAAAAg/ytJ7GzMIKRcAMAAACAUeDNIPRgb8jCAAAAAElFTkSuQmCC", - "text/plain": [ - "
" + "cell_type": "markdown", + "metadata": { + "id": "Niaqr4eUv1zT" + }, + "source": [ + "## Problem Statement\n", + "The following data were obtained in a constant-pressure filtration unit for filtration of a yeast suspension:\n", + "\n", + "t (min) | V (L filtrate)\n", + "-------------------|------------------\n", + "4 | 115\n", + "20 | 365\n", + "48 | 680\n", + "76 | 850\n", + "120 | 1130\n", + "\n", + "\n", + "Characteristics of the filter are as follows:
\n", + "$A = 0.28 \text{m^2}$, $C = 1920 \text{kg/m}^3$, $𝜇 = 2.9ᐧ10^{-3} \text{kg/mᐧs}$, $𝛼 = 4 \text{m/kg}$ \n", + "\n", + "1. Determine the pressure drop across the filter.\n", + "2. Determine the filter medium resistance ($r_{m}$).\n", + "3. Determine the size of the filter for the same pressure drop to process 4000 L of cell suspension in 20 minutes.\n", + "\n", + "**Do the following throughout this notebook:**\n", + "1. Propose a mathematical model for the given problem statement.\n", + "2. Solve the problem analytically.\n", + "3. Write pseudocode before using Python.\n", + "4. Solve the problem using Python.\n", + "5. Print answers to the screen.\n", + "6. Answer discussion questions." ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "# Make a scatter plot\n", - "### BEGIN SOLUTION\n", - "# Publication quality guidelines\n", - "figure(figsize=(4, 4), dpi=300)\n", - "plt.scatter(V, t_over_V, marker=\"8\", linewidths=3)\n", - "plt.tick_params(axis=\"both\", direction=\"in\", which=\"major\", labelsize=15)\n", - "plt.tick_params(axis=\"both\", direction=\"in\", which=\"minor\", labelsize=15)\n", - "plt.title(\"Visualizing Ruth Equation\", fontsize=16, weight=\"bold\")\n", - "plt.xlabel(\"V [L]\", fontsize=16, weight=\"bold\")\n", - "plt.ylabel(\"t/V [min/L]\", fontsize=16, weight=\"bold\")\n", - "### END SOLUTION\n", - "\n", - "# Calculate the trendline equation\n", - "z = np.polyfit(V, t_over_V, 1)\n", - "p = np.poly1d(z)\n", - "\n", - "# Plot the trendline and display equation as title\n", - "plt.plot(V, p(V), \"r--\")\n", - "print(\"The equation of the linear trendline is:\", \"y=%.6fx+%.6f\" % (z[0], z[1]))\n", - "\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "EiG1sw0ORA7b" - }, - "source": [ - "The title of the graph is the equation of the linear regression line. Use it to store the value of the Ruth coefficient in the variable `K`. Additionally convert the Ruth coefficient to SI units and store in the variable `K_SI`. Note that the slope of the linear trendline is stored in `z[0]`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" }, - "id": "gKQad0YCRF0i", - "outputId": "d55249b8-568c-494c-8aa6-b4ecec37af81" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "The Ruth coefficient is 0.00023792325864104675 m^6 / s\n" - ] - } - ], - "source": [ - "# Calculating K using slope from graph\n", - "### BEGIN SOLUTION\n", - "K = 1 / z[0] # L^2/min\n", - "\n", - "# convert to SI units\n", - "\n", - "K_SI = (K * (1e-6)) / 60 # m^6/s\n", - "\n", - "print(\"The Ruth coefficient is\", K_SI, \"m^6 / s\")\n", - "### END SOLUTION" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "95pYTMpMRIH_" - }, - "source": [ - "Use the Ruth coefficient to solve for the pressure drop across the filter, $ΔP$, and store it in the variable `deltaP`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" + "cell_type": "markdown", + "metadata": { + "id": "EK7yingcviST" + }, + "source": [ + "#1. Governing Equations for the Filtration System\n", + "\n", + "Consider the following for separation of constant-pressure filtration:\n", + "\n", + "For filtration, the **rate** (*the flow of filtrate*) for a **constant-pressure** (*vacuum*) filtration operation is determined by the **resistance of the cake** and the **filter medium**.\n", + "## 1.1 Flow Rate Equation\n", + " We can understand this by setting up a differential equation to model this relationship:\n", + "\n", + "$$Equation (11.1):\\frac{dV}{dt} = \\frac{g_{c}ΔPA}{(r_{m}+r_{c})𝜇}$$\n", + "\n", + "where:\n", + "\n", + "\n", + "$V$ = volume of filtrate\n", + "\n", + "$A$ = surface area of the filter\n", + "\n", + "$ΔP$ = pressure-drop through the cake and filter medium\n", + "\n", + "$𝜇$ = viscosity of the filtrate\n", + "\n", + "$r_{m}$ = resistance of the filter medium\n", + "\n", + "$r_{c}$ = resistance of the cake
\n", + "\n", + "\n", + "\n", + "## 1.3 Filter Medium Resistance Equation\n", + "The value of $r_{m}$ is characteristic of the filter medium. However the cake resistance $r_{c}$, increases during filtration and after a start-up period, $r_{c}$ exceeds $r_{m}$. The value of $r_{c}$ is given by:\n", + "\n", + "$$ Equation (11.2): r_{c} = 𝛼\\frac{W}{A} = 𝛼\\frac{CV}{A} $$\n", + "\n", + "where:\n", + "\n", + "$W$ = total weight of the cake on filter\n", + "\n", + "$C$ = weight of the cake deposited per unit volume of filtrate\n", + "\n", + "$𝛼$ = average specific resistance of the cake\n", + "\n", + "\n", + "## 1.4 Cake Weight Equation\n", + "The total weight of the cake is related to the total volume of filtrate and can be characterized by:\n", + "

\n", + "$$Equation (11.3): W = CV$$\n", + "\n", + "where:\n", + "\n", + "$W$ = total weight of the cake on filter\n", + "\n", + "$C$ = weight of the cake deposited per unit volume of filtrate\n", + "\n", + "$V$ = volume of filtrate\n" + ] }, - "id": "PFNq-XmxAprf", - "outputId": "399aa370-c8a4-4b8a-c979-dcb77efbd7c9" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "The pressure drop is 0.0034484503959635255 N/m^2\n" - ] - } - ], - "source": [ - "# Constants from the problem statement\n", - "alpha = 4 # m/kg\n", - "C = 1920 # kg/m^3\n", - "mu = 2.9e-3 # kg/m*s\n", - "A = 0.28 # m^2\n", - "gc = 9.8 # kgf / kg*m *s^2\n", - "\n", - "### BEGIN SOLUTION\n", - "deltaP = (K_SI * alpha * C * mu) / (2 * (A**2) * gc)\n", - "print(\"The pressure drop is\", deltaP, \"N/m^2\")\n", - "### END SOLUTION" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "9bFlB9tlSN_7" - }, - "source": [ - "How can we use the plot from 1.4 to illustrate our transformed data?" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "FbF24bm5ArAK" - }, - "source": [ - "## Determine the Filter Medium Resistance\n", - "\n", - "Calculate the value of $r_{m}$ from the y intercept in Figure 1.4 given:\n", - "\n", - "$y$ $intercept = \\frac{2V_{0}}{K}$\n", - "\n", - "$r_{m} = \\frac{𝛼V_{0}C}{A}$\n", - "\n", - "Use the y-intercept from the Ruth equation plot to determine the resistance of the filter medium $r_{m}$ and store it in the variable `r_m`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" + "cell_type": "markdown", + "metadata": { + "id": "z4mx6zRK5HA7" + }, + "source": [ + "# 2. Describe the Filtration System Analytically\n", + "\n" + ] }, - "id": "xvHgizq_Au0u", - "outputId": "84d1979d-65f0-4ce4-fa33-a0262c4f547d" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "The resistance of the filter medium is 5317.806000864924 m^-1\n" - ] - } - ], - "source": [ - "### BEGIN SOLUTION\n", - "# read y-int. from plot\n", - "yint = z[1] # = 2V0/K\n", - "\n", - "# Calculate V0\n", - "V0 = (yint * K) / 2\n", - "\n", - "# Convert V0 to SI units\n", - "V0_SI = V0 * 1e-3 # m^3\n", - "\n", - "# Solve for r_m\n", - "r_m = (alpha * V0_SI * C) / A\n", - "### END SOLUTION\n", - "\n", - "print(\"The resistance of the filter medium is\", r_m, \"m^-1\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "MwWRD1_cAwIP" - }, - "source": [ - "## Determine the Size of the Filter \n", - "\n", - "For the same pressure drop, determine the size of the filter to process 4000 L of cell suspension in 20 minutes.\n", - "\n", - "> **Hint**: Use the Ruth equation to calculate the required area using:\n", - "$V^{2} + 2VV_{0} = Kt $\n", - "\n", - "where $K = (\\frac{2A^{2}}{𝛼C𝜇})Δpg_{c}$ and $V_{0} = \\frac{r_{m}}{𝛼C}A$. \n", - "\n", - "**Record your answer using paper and pencil.**\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "DimdzD-4RY1g" - }, - "source": [ - "Use your values of $V$, $ΔP$, and $t$ to determine the size of the filter.
\n", - ">**Hint**: Reformat the Ruth equation to be in terms of surface area only. This should produce a quadratic function. Solve the quadratic equation.\n", - "\n", - "**Pencil and Paper Solution:**\n", - "\n", - "$$\\left[\\left(\\frac{2A^{2}}{𝛼C𝜇}\\right)Δpg_{c}\\right](t) - 2V\\left(\\frac{r_{m}}{𝛼C}A\\right) -V^2 = 0$$\n", - "\n", - "$$3.59A^2-5.54A -8 = 0$$\n", - "\n", - "$$A = 2.45 m^2$$" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "t2VxSGC3T_nA" - }, - "source": [ - "Let's use Python as a calculator to check our answer." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" + "cell_type": "markdown", + "metadata": { + "id": "WER8ry9DQukM" + }, + "source": [ + "With the information given in Section 1, we can now use these equations to define the mathematical relationship to solve the problem.\n", + "\n", + "Use the previous three equations (11.1), (11.2), and (11.3) to analytically derive the Ruth equation:

$$ Equation (11.6): \\frac{t}{V} = \\frac{1}{K}(V+2V_{0})$$
\n", + "where $V_{0}$ and $K$, the Ruth coefficient, are defined as:

\n", + "$V_{0} = \\frac{r_{m}}{𝛼C}A$ ** ** $K = (\\frac{2A^{2}}{𝛼C𝜇})Δpg_{c}$
\n", + "\n", + " Record your answer using pencil and paper. \n", + "\n", + ">**Hint**: You will need to solve for two equations (11.4) and (11.5) before deriving the Ruth equation.
\n", + "
How can you transform a differential equation into a linear equation?
What bounds of $V$ and $t$ will you need to integrate within?" + ] }, - "id": "7CM9RSB7IaDU", - "outputId": "af7cd767-d148-434d-a280-ebee38bd6502" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "The possible areas of the filter are: 2.4519924278922605 -0.9088169404270792 m^2\n" - ] - } - ], - "source": [ - "### BEGIN SOLUTION\n", - "# Use quadratic equation to solve for the roots\n", - "root1 = (5.54 + math.sqrt((5.54**2) - (4 * 3.59 * -8))) / (2 * 3.59)\n", - "root2 = (5.54 - math.sqrt((5.54**2) - (4 * 3.59 * -8))) / (2 * 3.59)\n", - "### END SOLUTION\n", - "\n", - "print(\"The possible areas of the filter are:\", root1, root2, \"m^2\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "z-3Pw57o2H0t" - }, - "source": [ - "Only one root is positive, and since we cannot have a negative area, the positive root must be the solution." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "HbJHoaXtwNJI" - }, - "source": [ - "## Pressure Drop Optimization\n", - "\n", - "Lets say we wanted to reduce the pressure drop in the system by changing the flow rate of the filtrate. Inuitively, we can predict that an increase in velocity of the fitlrate across the filter will increase the pressure drop - but by how much?\n", - "\n", - "Use the original dataset and modulate it in such a way that the pressure drop decreases when the rest of the analysis is performed.\n", - "
\n", - "\n", - "> **Hint**: we can't speed up or slow down time, so we will have to change the flowrate.\n", - "\n", - "Now multiply the original list `V` by a scalar to change the data uniformly. Store the scaled data in the list `alt_V`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" + "cell_type": "code", + "source": [ + "### BEGIN SOLUTION" + ], + "metadata": { + "id": "6P0a22KupV2X" + }, + "execution_count": null, + "outputs": [] }, - "id": "gkcluSvfxT28", - "outputId": "c92b9fde-5fcb-405e-e431-90ea3641800b" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "The scaled flowrate data is: [1.1500000000000001, 3.65, 6.8, 8.5, 11.3] L/min\n" - ] - } - ], - "source": [ - "# use a hypothetical scaling factor\n", - "\n", - "### BEGIN SOLUTION\n", - "factor = 0.01\n", - "\n", - "# apply factor to each term of V\n", - "alt_V = [value * factor for value in V]\n", - "### END SOLUTION\n", - "\n", - "print(\"The scaled flowrate data is:\", alt_V, \"L/min\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "eB4saBfnx_up" - }, - "source": [ - "Write a `for` loop that repeats this analysis until the data produces the desired pressure drop for the system. Print how many iterations it takes to find your answer. Hint: your previous code will come in handy here! Your target $ΔP$ is $0.002 N/m^{2}$.\n", - "\n", - "`np.polyfit` and `np.poly1d` are useful tools for formulating linear trendlines using `numpy` arrays. More information is available here: (https://numpy.org/doc/stable/reference/generated/numpy.polyfit.html)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" + "cell_type": "markdown", + "metadata": { + "id": "Qzyt4ObhfqWl" + }, + "source": [ + "**Paper and Pencil Solution:**

\n", + "Substituting Equations (11.2) and (11.3) into Equation (11.1) we get:\n", + "

\n", + "$$Equation (11.4):\n", + "\\frac{d(V/A)}{dt} = \\frac{g_{c}Δp}{(r_{m}+𝛼\\frac{CV}{A})𝜇} $$\n", + "
\n", + "\n", + "Integrating Equation 11.4 from $V=0$ to $V=V$ and $t=0$ to $t=t$ gives:\n", + "

\n", + "$$Equation (11.5):\n", + "V^{2}+2VV_{0}=Kt$$\n", + "\n", + "where $V_{0}$ and $K$ are defined as:\n", + "\n", + "$$V_{0} = \\frac{r_{m}}{𝛼C}A$$\n", + "\n", + "and\n", + "\n", + "$$K = (\\frac{2A^{2}}{𝛼C𝜇})Δpg_{c}$$\n", + "\n", + "For a constant-pressure filtration, 11.5 is known as the Ruth equation and we can rearrange it to get:\n", + "

\n", + "$$Equation (11.6):\n", + "\\frac{t}{V} = \\frac{1}{K}(V+2V_{0})$$" + ] + }, + { + "cell_type": "code", + "source": [ + "### END SOLUTION" + ], + "metadata": { + "id": "6yW5uXw_pbgT" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Mtdg1Tk0oJqp" + }, + "source": [ + "The integration performed here could also be modeled in Python as outlined in this notebook: (https://ndcbe.github.io/data-and-computing/notebooks/07/Systems-of-Differential-Equations-and-Scipy.html#systems-of-differential-equations)" + ] }, - "id": "-HtOp-alyWB9", - "outputId": "9c396d4c-ce94-4269-d96a-59ad3aa137b6" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "The pressure drop is 0.0033798262330838502 N/m^2\n", - "The pressure drop is 0.0033118917602833696 N/m^2\n", - "The pressure drop is 0.0032446469775620794 N/m^2\n", - "The pressure drop is 0.003178091884919984 N/m^2\n", - "The pressure drop is 0.0031122264823570814 N/m^2\n", - "The pressure drop is 0.0030470507698733703 N/m^2\n", - "The pressure drop is 0.002982564747468853 N/m^2\n", - "The pressure drop is 0.0029187684151435285 N/m^2\n", - "The pressure drop is 0.0028556617728973943 N/m^2\n", - "The pressure drop is 0.0027932448207304557 N/m^2\n", - "The pressure drop is 0.0027315175586427073 N/m^2\n", - "The pressure drop is 0.002670479986634154 N/m^2\n", - "The pressure drop is 0.0026101321047047924 N/m^2\n", - "The pressure drop is 0.002550473912854621 N/m^2\n", - "The pressure drop is 0.002491505411083646 N/m^2\n", - "The pressure drop is 0.0024332265993918627 N/m^2\n", - "The pressure drop is 0.0023756374777792718 N/m^2\n", - "The pressure drop is 0.002318738046245873 N/m^2\n", - "The pressure drop is 0.0022625283047916682 N/m^2\n", - "The pressure drop is 0.0022070082534166547 N/m^2\n", - "The pressure drop is 0.0021521778921208354 N/m^2\n", - "The pressure drop is 0.002098037220904208 N/m^2\n", - "The pressure drop is 0.002044586239766773 N/m^2\n", - "The pressure drop is 0.0019918249487085302 N/m^2\n", - "The analysis took 24 iterations at a factor of 0.7599999999999998\n", - "The Ruth coefficient is 0.00013742447419106847\n" - ] + "cell_type": "markdown", + "metadata": { + "id": "eXUpJKfQ6cDo" + }, + "source": [ + "Additional information on these and similar properties of constant-pressure filtration can be found here: (https://pubs.acs.org/doi/pdf/10.1021/ie50306a024)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "O05WUdtJAi9n" + }, + "source": [ + "#3. Determine Pressure Drop Across the Filter\n", + "\n", + "\n", + "In order to determine the pressure drop across the filter, we have to apply the Ruth equation (11.6) to the data from the Problem Statement in 1.2 to get the new data.\n", + "\n", + "Linearize the Ruth equation into the form $y=mx+b$.\n", + "\n", + "**Record your answer using paper and pencil.**" + ] + }, + { + "cell_type": "code", + "source": [ + "### BEGIN SOLUTION" + ], + "metadata": { + "id": "xQXWrMJ6qIfB" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "icJdQD7tk6yv" + }, + "source": [ + "**Pencil and Paper Solution**:\n", + "\n", + "$$Equation (11.6):\n", + "\\frac{t}{V} = \\frac{1}{K}(V+2V_{0})$$\n", + "\n", + "Multiply out terms and group into linear form:\n", + "\n", + "$$\\frac{t}{V} = \\frac{V}{K}+ \\frac{2V_{0}}{K}$$\n" + ] + }, + { + "cell_type": "code", + "source": [ + "### END SOLUTION" + ], + "metadata": { + "id": "jpV5KamAqWPn" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1SF8irq3muVf" + }, + "source": ["Now we will use this linearized form to calculate our new data table.\n", + "
\n", + "**Record your answer using paper and pencil.**\n", + "\n", + "
\n", + "Plug in the values from the data in Problem Statement 1.2 into the linearized form of Equation 11.6:\n", + "\n", + "For example: $t=4$ and $V=115$,\n", + "\n", + "$\\frac{4}{115} = 0.0348 = 0.035$ \\\\\n", + "\n", + "$\\Rightarrow$ The rest of the values for $t$ and $V$ can be substituted into this equation as shown above to get the transformed data below:\n", + "\n", + "V (L) | t/V (min/L)\n", + "-------------------|------------------\n", + "115 | 0.035\n", + "365 | 0.055\n", + "680 | 0.071\n", + "850 | 0.089\n", + "1130 | 0.11\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "kJ55uR4gQzsD" + }, + "source": [ + "Now, we will plot the Ruth equation ( $V$ vs. $t/V$ ) which can be used to determine the Ruth coefficient $K$.\n", + "\n", + "> **Hint**: When the Ruth Equation in the form $y=mx+b$, what is the slope of the line? What is the y-intercept?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "x41vq0ZtQ0pp" + }, + "source": [ + "In Python, you will need to:\n", + "1. Store the data for $V$ and $t$ in a list and calculate $t/V$.\n", + "2. Write pseudocode to perform this operation.\n", + "3. Store the appropriate data in the lists called `t`, `V` and `t_over_V`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ckuAUutV5Ebf", + "outputId": "aa81440d-45bf-4067-e574-c747ba2bd7b0" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "The values of t/V are: [0.03478261 0.05479452 0.07058824 0.08941176 0.10619469]\n" + ] + } + ], + "source": [ + "#Calculating t/V\n", + "\n", + "#import data\n", + "### BEGIN SOLUTION\n", + "t = [4, 20, 48, 76, 120] #min\n", + "V = [115, 365, 680, 850, 1130] #L\n", + "### END SOLUTION\n", + "\n", + "#calculate t/V\n", + "### BEGIN SOLUTION\n", + "t_over_V = []\n", + "for i in range(len(t)):\n", + " t_over_V = np.append(t_over_V, t[i]/V[i])\n", + "### END SOLUTION\n", + "print(\"The values of t/V are:\",t_over_V)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "g1B7ca3CQ9IH" + }, + "source": [ + "Finish the code below to generate a scatter plot of $V$ vs. $t/V$. Give the axes appropriate titles.
\n", + "> **Hint**: matplotlib.pyplot has the built-in scatterplot tool `plt.scatter()`.\n", + "
\n", + "\n", + "* Additional information on matplotlib can be found here: https://ndcbe.github.io/data-and-computing/notebooks/01/Matplotlib.html#customizing-plots\n", + "* Additional information on plt.scatter() can be found here: https://ndcbe.github.io/data-and-computing/notebooks/09/Visualizing-Data.html#scatter-plots" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "sfLTzPVfQ96y" + }, + "outputs": [], + "source": [ + "#Make a scatter plot\n", + "### BEGIN SOLUTION\n", + "#Publication quality guidelines\n", + "figure(figsize=(4, 4), dpi=300)\n", + "plt.scatter(V,t_over_V,marker = '8', linewidths = 3)\n", + "plt.tick_params(axis='both',direction=\"in\", which='major', labelsize=15)\n", + "plt.tick_params(axis='both',direction=\"in\", which='minor', labelsize=15)\n", + "plt.title(\"Visualizing Ruth Equation\",fontsize=16, weight='bold')\n", + "plt.xlabel(\"V [L]\", fontsize=16, weight='bold')\n", + "plt.ylabel(\"t/V [min/L]\",fontsize=16, weight='bold')\n", + "### END SOLUTION\n", + "\n", + "#Calculate the trendline equation\n", + "z = np.polyfit(V, t_over_V, 1)\n", + "p = np.poly1d(z)\n", + "\n", + "#Plot the trendline and display equation as title\n", + "plt.plot(V, p(V), \"r--\")\n", + "print(\"The equation of the linear trendline is:\",\"y=%.6fx+%.6f\"%(z[0],z[1]))\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "EiG1sw0ORA7b" + }, + "source": [ + "The title of the graph is the equation of the linear regression line. Use it to store the value of the Ruth coefficient in the variable `K`. Additionally convert the Ruth coefficient to SI units and store in the variable `K_SI`. Note that the slope of the linear trendline is stored in `z[0]`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "gKQad0YCRF0i", + "outputId": "14116057-c689-4577-b194-33981186842d" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "The Ruth coefficient is 0.00023792325864104675 m^6 / s\n" + ] + } + ], + "source": [ + "#Calculating K using slope from graph\n", + "### BEGIN SOLUTION\n", + "K = 1/z[0] # L^2/min\n", + "\n", + "#convert to SI units\n", + "\n", + "K_SI = (K*(1E-6))/60 #m^6/s\n", + "\n", + "print(\"The Ruth coefficient is\",K_SI,\"m^6 / s\")\n", + "### END SOLUTION" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "95pYTMpMRIH_" + }, + "source": [ + "Use the Ruth coefficient to solve for the pressure drop across the filter, $ΔP$, and store it in the variable `deltaP`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "PFNq-XmxAprf", + "outputId": "5d49f9d4-7efd-42cd-97a9-8f4446e10b1c" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "The pressure drop is 0.0034484503959635255 N/m^2\n" + ] + } + ], + "source": [ + "#Constants from the problem statement\n", + "alpha = 4 #m/kg\n", + "C = 1920 #kg/m^3\n", + "mu = 2.9E-3 #kg/m*s\n", + "A = 0.28 #m^2\n", + "gc = 9.8 #kgf / kg*m *s^2\n", + "\n", + "### BEGIN SOLUTION\n", + "deltaP = (K_SI*alpha*C*mu)/(2*(A**2)*gc)\n", + "print(\"The pressure drop is\", deltaP, \"N/m^2\")\n", + "### END SOLUTION" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9bFlB9tlSN_7" + }, + "source": [ + "How can we use the plot from 1.4 to illustrate our transformed data?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "FbF24bm5ArAK" + }, + "source": [ + "## 3.1 Determine the Filter Medium Resistance\n", + "\n", + "Calculate the value of $r_{m}$ from the y intercept in Figure 1.4 given:\n", + "\n", + "$y$ $intercept = \\frac{2V_{0}}{K}$\n", + "\n", + "$r_{m} = \\frac{𝛼V_{0}C}{A}$\n", + "\n", + "Use the y-intercept from the Ruth equation plot to determine the resistance of the filter medium $r_{m}$ and store it in the variable `r_m`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "xvHgizq_Au0u", + "outputId": "8bd9ef66-a2d7-41cc-97b5-fb937f4668e0" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "The resistance of the filter medium is 5317.806000864924 m^-1\n" + ] + } + ], + "source": [ + "### BEGIN SOLUTION\n", + "# read y-int. from plot\n", + "yint = z[1] # = 2V0/K\n", + "\n", + "#Calculate V0\n", + "V0 = (yint*K)/2\n", + "\n", + "#Convert V0 to SI units\n", + "V0_SI = V0*1E-3 #m^3\n", + "\n", + "#Solve for r_m\n", + "r_m = (alpha*V0_SI*C)/A\n", + "### END SOLUTION\n", + "\n", + "print(\"The resistance of the filter medium is\", r_m, \"m^-1\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "MwWRD1_cAwIP" + }, + "source": [ + "## 3.2 Determine the Size of the Filter\n", + "\n", + "For the same pressure drop, determine the size of the filter to process 4000 L of cell suspension in 20 minutes.\n", + "\n", + "> **Hint**: Use the Ruth equation to calculate the required area using:\n", + "$V^{2} + 2VV_{0} = Kt $\n", + "\n", + "where $K = (\\frac{2A^{2}}{𝛼C𝜇})Δpg_{c}$ and $V_{0} = \\frac{r_{m}}{𝛼C}A$.\n", + "\n", + "**Record your answer using paper and pencil.**\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "DimdzD-4RY1g" + }, + "source": [ + "Use your values of $V$, $ΔP$, and $t$ to determine the size of the filter.
\n", + ">**Hint**: Reformat the Ruth equation to be in terms of surface area only. This should produce a quadratic function. Solve the quadratic equation.\n", + "\n", + "**Pencil and Paper Solution:**\n", + "\n", + "$$\\left[\\left(\\frac{2A^{2}}{𝛼C𝜇}\\right)Δpg_{c}\\right](t) - 2V\\left(\\frac{r_{m}}{𝛼C}A\\right) -V^2 = 0$$\n", + "\n", + "$$3.59A^2-5.54A -8 = 0$$\n", + "\n", + "$$A = 2.45 \text{m}^2$$" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "t2VxSGC3T_nA" + }, + "source": [ + "Let's use Python as a calculator to check our answer." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "7CM9RSB7IaDU", + "outputId": "95cccb58-1d8e-4b00-cd85-9c90c77f9922" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "The possible areas of the filter are: 2.4519924278922605 -0.9088169404270792 m^2\n" + ] + } + ], + "source": [ + "### BEGIN SOLUTION\n", + "#Use quadratic equation to solve for the roots\n", + "root1 = (5.54+math.sqrt((5.54**2)-(4*3.59*-8)))/(2*3.59)\n", + "root2 = (5.54-math.sqrt((5.54**2)-(4*3.59*-8)))/(2*3.59)\n", + "### END SOLUTION\n", + "\n", + "print(\"The possible areas of the filter are:\", root1,root2,\"m^2\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "z-3Pw57o2H0t" + }, + "source": [ + "Only one root is positive, and since we cannot have a negative area, the positive root must be the solution." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HbJHoaXtwNJI" + }, + "source": [ + "#4. Pressure Drop Optimization\n", + "\n", + "##4.1 Optimization via Sensitivity Analysis\n", + "Lets say we wanted to reduce the pressure drop in the system by changing the flow rate of the filtrate. Inuitively, we can predict that an increase in velocity of the filtrate across the filter will increase the pressure drop - but by how much? In order to do this, we'll compare three different optimization approches to see if there's a difference between them.\n", + "\n", + "Use the original dataset and modulate it in such a way that the pressure drop decreases when the rest of the analysis is performed.\n", + "
\n", + "\n", + "> **Hint**: we can't speed up or slow down time, so we will have to change the flowrate.\n", + "\n", + "Now multiply the original list `V` by a scalar to change the data uniformly. Store the scaled data in the list `alt_V`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "gkcluSvfxT28", + "outputId": "a6697c6b-3c82-444a-8f11-bd5918db7e30" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "The scaled flowrate data is: [1.1500000000000001, 3.65, 6.8, 8.5, 11.3] L/min\n" + ] + } + ], + "source": [ + "#use a hypothetical scaling factor\n", + "\n", + "### BEGIN SOLUTION\n", + "factor = 0.01\n", + "\n", + "#apply factor to each term of V\n", + "alt_V = [value * factor for value in V]\n", + "### END SOLUTION\n", + "\n", + "print(\"The scaled flowrate data is:\", alt_V,\"L/min\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "eB4saBfnx_up" + }, + "source": [ + "Write a `for` loop that repeats this analysis until the data produces the desired pressure drop for the system. Print how many iterations it takes to find your answer. Hint: your previous code will come in handy here! Your target $ΔP$ is $0.002 \text{N/m}^{2}$.\n", + "\n", + "`np.polyfit` and `np.poly1d` are useful tools for formulating linear trendlines using `numpy` arrays. More information is available here: (https://numpy.org/doc/stable/reference/generated/numpy.polyfit.html)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "-HtOp-alyWB9", + "outputId": "2912d79b-2ba7-4155-a692-1e73c2d5a83c" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "The pressure drop is 0.0033798262330838502 N/m^2\n", + "The pressure drop is 0.0033118917602833696 N/m^2\n", + "The pressure drop is 0.0032446469775620794 N/m^2\n", + "The pressure drop is 0.003178091884919984 N/m^2\n", + "The pressure drop is 0.0031122264823570814 N/m^2\n", + "The pressure drop is 0.0030470507698733703 N/m^2\n", + "The pressure drop is 0.002982564747468854 N/m^2\n", + "The pressure drop is 0.0029187684151435285 N/m^2\n", + "The pressure drop is 0.0028556617728973943 N/m^2\n", + "The pressure drop is 0.0027932448207304557 N/m^2\n", + "The pressure drop is 0.0027315175586427073 N/m^2\n", + "The pressure drop is 0.0026704799866341536 N/m^2\n", + "The pressure drop is 0.0026101321047047924 N/m^2\n", + "The pressure drop is 0.002550473912854621 N/m^2\n", + "The pressure drop is 0.002491505411083646 N/m^2\n", + "The pressure drop is 0.0024332265993918627 N/m^2\n", + "The pressure drop is 0.0023756374777792718 N/m^2\n", + "The pressure drop is 0.002318738046245873 N/m^2\n", + "The pressure drop is 0.0022625283047916682 N/m^2\n", + "The pressure drop is 0.0022070082534166547 N/m^2\n", + "The pressure drop is 0.0021521778921208354 N/m^2\n", + "The pressure drop is 0.002098037220904208 N/m^2\n", + "The pressure drop is 0.002044586239766773 N/m^2\n", + "The pressure drop is 0.0019918249487085302 N/m^2\n", + "The analysis took 24 iterations at a factor of 0.7599999999999998\n", + "The Ruth coefficient is 0.00013742447419106847\n" + ] + } + ], + "source": [ + "### BEGIN SOLUTION\n", + "#set boolean for loop\n", + "idealP = False\n", + "\n", + "#initialize counter and factor\n", + "count = 0\n", + "factor = 1\n", + "\n", + "#start loop\n", + "while idealP == False:\n", + "\n", + " #increment factor down\n", + " factor = factor - 0.01\n", + "\n", + " #store modified data in list\n", + " alt_V = [value * factor for value in V]\n", + "\n", + " #prep empty list for calculation\n", + " alt_t_over_V = []\n", + "\n", + " #for loop for calculating t/V using modified data\n", + " for i in range(len(t)):\n", + " alt_t_over_V = np.append(alt_t_over_V, t[i]/alt_V[i])\n", + "\n", + " #calculate the trendline\n", + " z = np.polyfit(alt_V, alt_t_over_V, 1)\n", + " p = np.poly1d(z)\n", + "\n", + " #Calculate K using trendline slope\n", + " K = 1/z[0] # L^2/min\n", + "\n", + " #convert to SI units\n", + " K_SI = (K*(1E-6))/60 #m^6/s\n", + "\n", + " #constants for system\n", + " alpha = 4 #m/kg\n", + " C = 1920 #kg/m^3\n", + " mu = 2.9E-3 #kg/m*s\n", + " A = 0.28 #m^2\n", + " gc = 9.8 #kgf / kg*m *s^2\n", + "\n", + " #Calculate pressure drop\n", + " deltaP = (K_SI*alpha*C*mu)/(2*(A**2)*gc)\n", + "\n", + " print(\"The pressure drop is\", deltaP, \"N/m^2\")\n", + "\n", + " #check if condition has been met and exit if so\n", + " if deltaP <= 0.002:\n", + " idealP = True\n", + "\n", + " #increment counter\n", + " count += 1\n", + "\n", + "### END SOLUTION\n", + "\n", + "#report iterations\n", + "print(\"The analysis took\", count,\" iterations at a factor of\", factor)\n", + "print(\"The Ruth coefficient is\", K_SI)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## 4.2 Optimization Using Newton's Method" + ], + "metadata": { + "id": "bXPngBMHvLzt" + } + }, + { + "cell_type": "markdown", + "metadata": { + "id": "bo3Mr8rHqr01" + }, + "source": [ + "The code above solved for a root using direct iteration, but it can also be done using Newton's method. Consider the following function needed to perform root-finding using Newton's method. This function and additional information on Newton's method can be found in this notebook: (https://ndcbe.github.io/data-and-computing/notebooks/06/Newton-Raphson-Method-in-One-Dimension.html)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "id": "FZj8GjpAq80U" + }, + "outputs": [], + "source": [ + "def newton(f,fprime,x0,epsilon=1.0e-6, LOUD=False, max_iter=50):\n", + " \"\"\"Find the root of the function f(x) via Newton-Raphson method\n", + " Args:\n", + " f: the function, in canoncial form, we want to fix the root of [Python function]\n", + " fprime: the derivative of f [Python function]\n", + " x0: initial guess [float]\n", + " epsilon: tolerance [float]\n", + " LOUD: toggle on/off print statements [boolean]\n", + " max_iter: maximum number of iterations [int]\n", + "\n", + " Returns:\n", + " estimate of root [float]\n", + " \"\"\"\n", + "\n", + " assert callable(f), \"Warning: 'f' should be a Python function\"\n", + " assert callable(fprime), \"Warning: 'fprime' should be a Python function\"\n", + " assert type(x0) is float or type(x0) is int, \"Warning: 'x0' should be a float or integer\"\n", + " assert type(epsilon) is float, \"Warning: 'eps' should be a float\"\n", + " assert type(max_iter) is int, \"Warning: 'max_iter' should be an integer\"\n", + " assert max_iter >= 0, \"Warning: 'max_iter' should be non-negative\"\n", + "\n", + " x = x0\n", + " if (LOUD):\n", + " print(\"x0 =\",x0)\n", + " iterations = 0\n", + " converged = False\n", + "\n", + " # Check if the residual is close enough to zero\n", + " while (not converged and iterations < max_iter):\n", + "\n", + " if (LOUD):\n", + " print(\"x_\",iterations+1,\"=\",x,\"-\",f(x),\"/\",\n", + " fprime(x),\"=\",x - f(x)/fprime(x))\n", + " ### BEGIN SOLUTION\n", + " #single-variable Newton's method equation\n", + " x = x - f(x)/fprime(x)\n", + " ### END SOLUTION\n", + " # check if converged\n", + " if np.fabs(f(x)) < epsilon:\n", + " converged = True\n", + "\n", + " iterations += 1\n", + " print(\"It took\",iterations,\"iterations\")\n", + "\n", + " if not converged:\n", + " print(\"Warning: Not a solution. Maximum number of iterations exceeded.\")\n", + " return x #return estimate of root" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "asuUuhfuvNh8" + }, + "source": [ + "Using these functions, determine what the Ruth constant in SI units `K_SI` will be when the pressure drop $ΔP$ is $0.002 N/m^{2}$.\n", + "> **Hint**: Begin by making two functions to satisfy the terms of the Newton's method function: `Nonlinear_function`, which will return the canonical form of the equation we used above to solve for $ΔP$, and `Dnonlinear_function`, which returns the derivative of `Nonlinear function` with respect to the variable being solved for (`K_SI`)." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "qJaTNCvLvhcn", + "outputId": "29e6a751-6412-402b-cf1a-da1ba5a6f0ff" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "x0 = 1\n", + "x_ 1 = 1 - -14.491960849645977 / -14.493960849645978 = 0.00013798850574719967\n", + "It took 1 iterations\n", + "0.00013798850574719967\n" + ] + } + ], + "source": [ + "deltaP = 0.002 #N/m^2\n", + "\n", + "#Constants for system\n", + "alpha = 4 #m/kg\n", + "C = 1920 #kg/m^3\n", + "mu = 2.9E-3 #kg/m*s\n", + "A = 0.28 #m^2\n", + "gc = 9.8 #kgf / kg*m *s^2\n", + "\n", + "### BEGIN SOLUTION\n", + "#Define pressure drop formula in canonical form\n", + "def Dnonlinear_function(x):\n", + " ''' compute 1st derivative of nonlinear function for demonstration\n", + " Arguments:\n", + " x: scalar\n", + " Returns:\n", + " c'(x): scalar\n", + " '''\n", + " return -(alpha*C*mu)/(2*(A**2)*gc)\n", + "\n", + "def Nonlinear_function(K_SI):\n", + " ''' compute 1st derivative of nonlinear function for demonstration\n", + " Arguments:\n", + " x: scalar\n", + " Returns:\n", + " c'(x): scalar\n", + " '''\n", + " return deltaP - (K_SI*alpha*C*mu)/(2*(A**2)*gc)\n", + "\n", + "### END SOLUTION\n", + "root = newton(Nonlinear_function,Dnonlinear_function,1,LOUD=True,max_iter=15)\n", + "print(root)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "c2oJjp7Ay2Pn" + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8fxHqVJ2TiAf" + }, + "source": [ + "In other systems, Newton's method will take several iterations to converge to a solution. Why do you think our system requires only one iteration to be solved?\n", + "\n", + "**Your answer:**" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## 4.3 Optimization using Inexact Newton\n", + "\n", + "Seeing how Newton's method only needed one iteration for this system, let's try to use Inexact Newton to compare how many iterations it takes to solve." + ], + "metadata": { + "id": "mhKKIZFv6Qs8" + } + }, + { + "cell_type": "code", + "source": [ + "#Calculate V0\n", + "V0 = (yint*K)/2\n", + "\n", + "#Convert V0 to SI units\n", + "V0_SI = V0*1E-3 #m^3\n", + "\n", + "### BEGIN SOLUTION\n", + "#Define pressure drop formula in canonical form\n", + "def dnonlinear_function2(x):\n", + " ''' compute 1st derivative of nonlinear function for demonstration\n", + " Arguments:\n", + " x: scalar\n", + " Returns:\n", + " c'(x): scalar\n", + " '''\n", + " return (gc*deltaP)/(((alpha*C)/A)*(V+V0)*mu)\n", + "\n", + "def nonlinear_function2(K_SI):\n", + " ''' compute 1st derivative of nonlinear function for demonstration\n", + " Arguments:\n", + " x: scalar\n", + " Returns:\n", + " c'(x): scalar\n", + " '''\n", + " return deltaP - (K_SI*alpha*C*mu)/(2*(A**2)*gc)\n", + "\n", + "### END SOLUTION\n", + "root = newton(Nonlinear_function,Dnonlinear_function,1,LOUD=True,max_iter=15)\n", + "print(root)\n", + "\n", + "def inexact_newton(f, fprime, x0, delta=1.0e-7, epsilon=1.0e-6, LOUD=False, max_iter=50):\n", + " \"\"\"Find the root of the function f via Newton-Raphson method\n", + " Args:\n", + " f: function to find root of [function]\n", + " x0: initial guess [float]\n", + " delta: finite difference parameter [float]\n", + " epsilon: tolerance [float]\n", + " LOUD: toggle on/off print statements [boolean]\n", + " max_iter: maximum number of iterations [int]\n", + "\n", + " Returns:\n", + " estimate of root [float]\n", + " \"\"\"\n", + "\n", + " assert callable(f), \"Warning: 'f' should be a Python function\"\n", + " assert type(x0) is float or type(x0) is int, \"Warning: 'x0' should be a float or integer\"\n", + " assert type(delta) is float, \"Warning: 'delta' should be a float\"\n", + " assert type(epsilon) is float, \"Warning: 'epsilon' should be a float\"\n", + " assert type(max_iter) is int, \"Warning: 'max_iter' should be an integer\"\n", + " assert max_iter >= 0, \"Warning: 'max_iter' should be non-negative\"\n", + "\n", + " x = x0\n", + " if LOUD:\n", + " print(\"x0 =\", x0)\n", + " iterations = 0\n", + " converged = False\n", + "\n", + " # Check if the residual is close enough to zero\n", + " while not converged and iterations < max_iter:\n", + " # Evaluate function 'f' at new 'x'\n", + " fx = f(x)\n", + " ### BEGIN SOLUTION\n", + " # Calculate 'slope' using finite differences\n", + " fxdelta = f(x + delta)\n", + " slope = (fxdelta - fx) / delta\n", + " ### END SOLUTION\n", + " if LOUD:\n", + " print(\"x_\", iterations + 1, \"=\", x, \"-\", fx, \"/\", slope, \"=\", x - fx / slope)\n", + "\n", + " x = x - fx / slope\n", + " iterations += 1\n", + "\n", + " # Check if converged\n", + " if np.fabs(f(x)) < epsilon:\n", + " converged = True\n", + "\n", + " print(\"It took\", iterations, \"iterations\")\n", + "\n", + " if not converged:\n", + " print(\"Warning: Not a solution. Maximum number of iterations exceeded.\")\n", + "\n", + " # Return estimate of root\n", + " return x\n" + ], + "metadata": { + "id": "9bySu21A6QHq" + }, + "execution_count": 10, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "root2 = inexact_newton(Nonlinear_function, Dnonlinear_function, 1000000, LOUD=True, max_iter=15)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "-6eno_NtJ3oS", + "outputId": "10147f37-6cb1-4ac7-ea93-1b73aaa6c36c" + }, + "execution_count": 46, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "x0 = 1000000\n", + "x_ 1 = 1000000 - -14493960.847645978 / -14.472752809524536 = -1465.376932817162\n", + "x_ 2 = -1465.376932817162 - 21239.117894226252 / -14.49396222596988 = -1.1614006325544324e-06\n", + "x_ 3 = -1.1614006325544324e-06 - 0.002016833295298998 / -14.493960849644612 = 0.00013798850574713958\n", + "It took 3 iterations\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Now that you've tested that, try to use an initial guess that is extremely far from the correct answer (I would suggest starting orders of magnitude away to begin). Does the code still converge to the correct answer? How many iterations did it take?\n", + "\n", + "**Your answer:**" + ], + "metadata": { + "id": "I678-NKbYntI" + } + }, + { + "cell_type": "markdown", + "source": [ + "## 4.4 Optimization using Secant Method\n", + "\n", + "Now, let's repeat what we did with Inexact Newton by adding an additional step and use the Secant method. Try to see if you can use even more extreme guesses compared to what was used in the previous section.\n", + "\n" + ], + "metadata": { + "id": "CQ_o2ym1Msz_" + } + }, + { + "cell_type": "code", + "source": [ + "def secant(f, fprime, x0, delta=1.0, epsilon=1.0e-6, LOUD=False, max_iter=50):\n", + " \"\"\"Find the root of the function f via the secant method\n", + " Args:\n", + " f: function to find root of [function]\n", + " x0: initial guess [float]\n", + " delta: finite difference parameter [float]\n", + " epsilon: tolerance [float]\n", + " LOUD: toggle on/off print statements [boolean]\n", + " max_iter: maximum number of iterations [int]\n", + "\n", + " Returns:\n", + " estimate of root [float]\n", + " \"\"\"\n", + "\n", + " assert callable(f), \"Error: 'f' should be a Python function\"\n", + " assert type(x0) is float or type(x0) is int, \"Error: 'x0' should be a float or integer\"\n", + " assert type(delta) is float, \"Error: 'delta' should be a float\"\n", + " assert type(epsilon) is float, \"Error: 'epsilon' should be a float\"\n", + " assert type(max_iter) is int, \"Error: 'max_iter' should be an integer\"\n", + " assert max_iter >= 0, \"Error: 'max_iter' should be non-negative\"\n", + "\n", + " x = x0\n", + " if LOUD:\n", + " print(\"x0 =\", x0)\n", + "\n", + " # First time use inexact Newton\n", + " x_old = x\n", + " fold = f(x_old)\n", + " # fx = fold\n", + "\n", + " slope = (f(x_old + delta) - fold) / delta\n", + " x = x - fold / slope\n", + "\n", + " fx = f(x)\n", + "\n", + " iterations = 1\n", + "\n", + " # Now switch to the secant method\n", + " while np.fabs(fx) > epsilon and iterations < max_iter:\n", + "\n", + " ### BEGIN SOLUTION\n", + "\n", + " # fold, and x_old correspond to the 2nd to last point\n", + " slope = (fx - fold) / (x - x_old)\n", + "\n", + " # Switch the history, overwrite fold with fx, etc.\n", + " fold = fx\n", + " x_old = x\n", + " if LOUD:\n", + " print(\"x_\", iterations + 1, \"=\", x, \"-\", fx, \"/\", slope, \"=\",\n", + " x - fx / slope)\n", + "\n", + " # Calculate new point\n", + " x = x - fx / slope\n", + "\n", + " ### END SOLUTION\n", + "\n", + " # Evaluate function f at new point\n", + " fx = f(x)\n", + " iterations += 1\n", + "\n", + " print(\"It took\", iterations, \"iterations\")\n", + "\n", + " if iterations >= max_iter:\n", + " print(\"Warning: Maximum number of iterations exceeded.\")\n", + " return x # Return estimate of root\n" + ], + "metadata": { + "id": "YPymd192MtR2" + }, + "execution_count": 48, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "root3 = secant(Nonlinear_function, Dnonlinear_function, 1000000, LOUD=True, max_iter=15)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "aPg1WPcqM7Cm", + "outputId": "af70248d-ce4d-44d4-b9b9-28f7b3801d47" + }, + "execution_count": 49, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "x0 = 1000000\n", + "x_ 2 = 0.00015832798089832067 - -0.00029479955654375606 / -14.493960849645978 = 0.00013798850574712646\n", + "It took 2 iterations\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "iJwZu0jXIaue" + }, + "source": [ + "#5. Discussion Questions\n", + "\n", + "For the following questions, answer with 2-3 sentences each.\n", + "\n", + "1. How does setting up the mathematical model help us solve the problem?\n", + "By linearizing the model, we make it easy to solve using optimization. This also would translate into making it more efficient to solve with a much more complex equation/set of data.\n", + "2. Can we define the Ruth equation without understanding the mathematical relationship between the variables?\n", + "No. This is because without understanding the mathematical relationship, it would be difficult to understand how to linearize the model.\n", + "3. Why is the plot useful for understanding the relationship between the data? How does the plot help us determine the filter medium resistance?\n", + "It helps us visually see the ruth equation and what its slope means. It also helps understand the relationship between volume and inverse flow rate.\n", + "4. Why is it important that the pressure drop is the same when determining the size of the filter?\n", + "If the pressure drop changes, then there are certain parameters about your system that you need to recalculate.\n", + "5. What can we infer about the effect of filter medium resistance on yeast cake size over time?\n", + "That the resistance increases. As time goes on, there is more debris building up on it over time.\n", + "6. How can the filter be optimized to reduce pressure drop?\n", + "The filter can be optimized by adjusting muiltiple physical properties of it while keeping the pressure drop the same.\n", + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "m3ZfoBXqy2P1" + }, + "outputs": [], + "source": [] } - ], - "source": [ - "### BEGIN SOLUTION\n", - "# set boolean for loop\n", - "idealP = False\n", - "\n", - "# initialize counter and factor\n", - "count = 0\n", - "factor = 1\n", - "\n", - "# start loop\n", - "while idealP == False:\n", - " # increment factor down\n", - " factor = factor - 0.01\n", - "\n", - " # store modified data in list\n", - " alt_V = [value * factor for value in V]\n", - "\n", - " # prep empty list for calculation\n", - " alt_t_over_V = []\n", - "\n", - " # for loop for calculating t/V using modified data\n", - " for i in range(len(t)):\n", - " alt_t_over_V = np.append(alt_t_over_V, t[i] / alt_V[i])\n", - "\n", - " # calculate the trendline\n", - " z = np.polyfit(alt_V, alt_t_over_V, 1)\n", - " p = np.poly1d(z)\n", - "\n", - " # Calculate K using trendline slope\n", - " K = 1 / z[0] # L^2/min\n", - "\n", - " # convert to SI units\n", - " K_SI = (K * (1e-6)) / 60 # m^6/s\n", - "\n", - " # constants for system\n", - " alpha = 4 # m/kg\n", - " C = 1920 # kg/m^3\n", - " mu = 2.9e-3 # kg/m*s\n", - " A = 0.28 # m^2\n", - " gc = 9.8 # kgf / kg*m *s^2\n", - "\n", - " # Calculate pressure drop\n", - " deltaP = (K_SI * alpha * C * mu) / (2 * (A**2) * gc)\n", - "\n", - " print(\"The pressure drop is\", deltaP, \"N/m^2\")\n", - "\n", - " # check if condition has been met and exit if so\n", - " if deltaP <= 0.002:\n", - " idealP = True\n", - "\n", - " # increment counter\n", - " count += 1\n", - "\n", - "### END SOLUTION\n", - "\n", - "# report iterations\n", - "print(\"The analysis took\", count, \" iterations at a factor of\", factor)\n", - "print(\"The Ruth coefficient is\", K_SI)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "bo3Mr8rHqr01" - }, - "source": [ - "The code above solved for a root using direct iteration, but it can also be done using Newton's method. Consider the following function needed to perform root-finding using Newton's method. This function and additional information on Newton's method can be found in this notebook: (https://ndcbe.github.io/data-and-computing/notebooks/06/Newton-Raphson-Method-in-One-Dimension.html)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "FZj8GjpAq80U" - }, - "outputs": [], - "source": [ - "def newton(f, fprime, x0, epsilon=1.0e-6, LOUD=False, max_iter=50):\n", - " \"\"\"Find the root of the function f(x) via Newton-Raphson method\n", - " Args:\n", - " f: the function, in canoncial form, we want to fix the root of [Python function]\n", - " fprime: the derivative of f [Python function]\n", - " x0: initial guess [float]\n", - " epsilon: tolerance [float]\n", - " LOUD: toggle on/off print statements [boolean]\n", - " max_iter: maximum number of iterations [int]\n", - "\n", - " Returns:\n", - " estimate of root [float]\n", - " \"\"\"\n", - "\n", - " assert callable(f), \"Warning: 'f' should be a Python function\"\n", - " assert callable(fprime), \"Warning: 'fprime' should be a Python function\"\n", - " assert (\n", - " type(x0) is float or type(x0) is int\n", - " ), \"Warning: 'x0' should be a float or integer\"\n", - " assert type(epsilon) is float, \"Warning: 'eps' should be a float\"\n", - " assert type(max_iter) is int, \"Warning: 'max_iter' should be an integer\"\n", - " assert max_iter >= 0, \"Warning: 'max_iter' should be non-negative\"\n", - "\n", - " x = x0\n", - " if LOUD:\n", - " print(\"x0 =\", x0)\n", - " iterations = 0\n", - " converged = False\n", - "\n", - " # Check if the residual is close enough to zero\n", - " while not converged and iterations < max_iter:\n", - " if LOUD:\n", - " print(\n", - " \"x_\",\n", - " iterations + 1,\n", - " \"=\",\n", - " x,\n", - " \"-\",\n", - " f(x),\n", - " \"/\",\n", - " fprime(x),\n", - " \"=\",\n", - " x - f(x) / fprime(x),\n", - " )\n", - "\n", - " # single-variable Newton's method equation\n", - " x = x - f(x) / fprime(x)\n", - "\n", - " # check if converged\n", - " if np.fabs(f(x)) < epsilon:\n", - " converged = True\n", - "\n", - " iterations += 1\n", - " print(\"It took\", iterations, \"iterations\")\n", - "\n", - " if not converged:\n", - " print(\"Warning: Not a solution. Maximum number of iterations exceeded.\")\n", - " return x # return estimate of root" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "asuUuhfuvNh8" - }, - "source": [ - "Using these functions, determine what the Ruth constant in SI units `K_SI` will be when the pressure drop $ΔP$ is $0.002 N/m^{2}$. \n", - "> **Hint**: Begin by making two functions to satisfy the terms of the Newton's method function: `Nonlinear_function`, which will return the canonical form of the equation we used above to solve for $ΔP$, and `Dnonlinear_function`, which returns the derivative of `Nonlinear function` with respect to the variable being solved for (`K_SI`)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { + ], + "metadata": { "colab": { - "base_uri": "https://localhost:8080/" + "provenance": [] }, - "id": "qJaTNCvLvhcn", - "outputId": "7fbf7c56-126d-432b-cff0-7d7460f7ca0f" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "x0 = 1\n", - "x_ 1 = 1 - -14.491960849645977 / -14.493960849645978 = 0.00013798850574719967\n", - "It took 1 iterations\n", - "0.00013798850574719967\n" - ] + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.12" } - ], - "source": [ - "deltaP = 0.002 # N/m^2\n", - "\n", - "# Constants for system\n", - "alpha = 4 # m/kg\n", - "C = 1920 # kg/m^3\n", - "mu = 2.9e-3 # kg/m*s\n", - "A = 0.28 # m^2\n", - "gc = 9.8 # kgf / kg*m *s^2\n", - "\n", - "\n", - "### BEGIN SOLUTION\n", - "# Define pressure drop formula in canonical form\n", - "def Dnonlinear_function(x):\n", - " \"\"\"compute 1st derivative of nonlinear function for demonstration\n", - " Arguments:\n", - " x: scalar\n", - " Returns:\n", - " c'(x): scalar\n", - " \"\"\"\n", - " return -(alpha * C * mu) / (2 * (A**2) * gc)\n", - "\n", - "\n", - "def Nonlinear_function(K_SI):\n", - " \"\"\"compute 1st derivative of nonlinear function for demonstration\n", - " Arguments:\n", - " x: scalar\n", - " Returns:\n", - " c'(x): scalar\n", - " \"\"\"\n", - " return deltaP - (K_SI * alpha * C * mu) / (2 * (A**2) * gc)\n", - "\n", - "\n", - "### END SOLUTION\n", - "root = newton(Nonlinear_function, Dnonlinear_function, 1, LOUD=True, max_iter=15)\n", - "print(root)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "8fxHqVJ2TiAf" - }, - "source": [ - "In other systems, Newton's method will take several iterations to converge to a solution. Why do you think our system requires only one iteration to be solved?" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "iJwZu0jXIaue" - }, - "source": [ - "## Discussion Questions\n", - "\n", - "For the following questions, answer with 2-3 sentences each.\n", - "\n", - "1. How does setting up the mathematical model help us solve the problem?\n", - "\n", - "2. Can we define the Ruth equation without understanding the mathematical relationship between the variables?\n", - "\n", - "3. Why is the plot useful for understanding the relationship between the data? How does the plot help us determine the filter medium resistance?\n", - "\n", - "4. Why is it important that the pressure drop is the same when determining the size of the filter?\n", - "\n", - "5. What can we infer about the effect of filter medium resistance on yeast cake size over time?\n", - "\n", - "6. How can the filter be optimized to reduce pressure drop?\n", - "\n", - "7. This problem assumes a homogeneous slurry being passed through the filter. What effect would an unevenly distributed or clumpy mixture have on the properties of the filter? How would the plot derived in Section 1.4 change?\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "colab": { - "provenance": [] - }, - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "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.12" - } - }, - "nbformat": 4, - "nbformat_minor": 4 + "nbformat": 4, + "nbformat_minor": 0 }