{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"tags": [
"header",
"hide-cell"
]
},
"outputs": [],
"source": [
"###############################################################################\n",
"# The Institute for the Design of Advanced Energy Systems Integrated Platform\n",
"# Framework (IDAES IP) was produced under the DOE Institute for the\n",
"# Design of Advanced Energy Systems (IDAES).\n",
"#\n",
"# Copyright (c) 2018-2023 by the software owners: The Regents of the\n",
"# University of California, through Lawrence Berkeley National Laboratory,\n",
"# National Technology & Engineering Solutions of Sandia, LLC, Carnegie Mellon\n",
"# University, West Virginia University Research Corporation, et al.\n",
"# All rights reserved. Please see the files COPYRIGHT.md and LICENSE.md\n",
"# for full copyright and license information.\n",
"###############################################################################"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Mixer Unit Model with Ideal Property Package\n",
"Author: Anuja Deshpande \n",
"Maintainer: Brandon Paul \n",
"Updated: 2023-06-01 \n",
"\n",
"\n",
"![](mixer.svg)\n",
"\n",
"## Learning Outcomes\n",
"\n",
"- Demonstrate use of the Mixer unit model in IDAES\n",
"- Demonstrate different options available\n",
"\n",
"\n",
"## Problem Statement\n",
"\n",
"In this example, we will be mixing liquid benzene and liquid toluene streams to form a mixture. The inlet conditions are as follows:\n",
"\n",
"**Stream 1:**\n",
"\n",
"Benzene Flow Rate = 100 mol/s\n",
"\n",
"Pressure = 101325 Pa \n",
"\n",
"Temperature = 353 K\n",
"\n",
"**Stream 2**\n",
"\n",
"Toluene Flow Rate = 100 mol/s\n",
"\n",
"Pressure = 202650 Pa \n",
"\n",
"Temperature = 356 K\n",
"\n",
"We will look at two cases in this tutorial:\n",
"\n",
"* Case 1: Specify the number of inlets to the mixer, and set the `momentum_mixing` type set to \"minimize\"\n",
"\n",
"* Case 2: Specify the inlet names, and set `momentum_mixing` type set to \"equality\" (in this case, pressure will be specified for only one inlet stream)\n",
"\n",
"**Note: \n",
"When the momentum mixing type is set to 'minimize', the mixed stream pressure takes the minimum value among all inlet stream pressures.\n",
"When the momentum mixing type is set to 'equality', the mixed stream, along with all inlet streams have the same value of pressure.**\n",
"\n",
"\n",
"For more details, please refer to the IDAES documentation: https://idaes-pse.readthedocs.io/en/stable"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Setting up the problem in IDAES"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In the following cell, we will be importing the necessary components from Pyomo and IDAES."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"# Import objects from pyomo package\n",
"from pyomo.environ import ConcreteModel, SolverFactory, value\n",
"\n",
"# Import the main FlowsheetBlock from IDAES. The flowsheet block will contain the unit model\n",
"from idaes.core import FlowsheetBlock\n",
"\n",
"# Import the mixer unit model\n",
"from idaes.models.unit_models import Mixer, MomentumMixingType\n",
"\n",
"# Import idaes logger to set output levels\n",
"import idaes.logger as idaeslog\n",
"\n",
"# Import the BTX_ideal property package to create a properties block for the flowsheet\n",
"from idaes.models.properties.activity_coeff_models import BTX_activity_coeff_VLE\n",
"\n",
"# Import the degrees_of_freedom function from the idaes.core.util.model_statistics package\n",
"# DOF = Number of Model Variables - Number of Model Constraints\n",
"from idaes.core.util.model_statistics import degrees_of_freedom\n",
"\n",
"# Create the ConcreteModel and the FlowsheetBlock objects, and attach the flowsheet block to it.\n",
"m = ConcreteModel()\n",
"\n",
"m.fs = FlowsheetBlock(\n",
" dynamic=False\n",
") # dynamic or ss flowsheet needs to be specified here\n",
"\n",
"# Add properties parameter block to the flowsheet with specifications\n",
"m.fs.properties = BTX_activity_coeff_VLE.BTXParameterBlock(\n",
" valid_phase=\"Liq\", activity_coeff_model=\"Ideal\"\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Case 1:\n",
"\n",
"Specify the number of inlets to the mixer, and set the `momentum_mixing` type set to \"minimize\". "
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The initial degrees of freedom is: 10\n"
]
}
],
"source": [
"# Create an instance of the mixer unit, attaching it to the flowsheet\n",
"# Specify that the property package to be used with the mixer is the\n",
"# one we created earlier, the number of mixer inlets is 2, and momentum\n",
"# mixing type is minimize\n",
"\n",
"m.fs.mixer_1 = Mixer(\n",
" property_package=m.fs.properties,\n",
" num_inlets=2,\n",
" momentum_mixing_type=MomentumMixingType.minimize,\n",
")\n",
"\n",
"# Call the degrees_of_freedom function, get initial DOF\n",
"DOF_initial = degrees_of_freedom(m)\n",
"print(\"The initial degrees of freedom is: {0}\".format(DOF_initial))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For case 1, we chose to specify only the number of inlets and names were not specified. When this option is selected, the inlets are named as \"inlet_1\", \"inlet_2\" and so on depending on the number of inlets specified. In the following cell, we will use this naming convention to specify the inlet conditions. "
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The final degrees of freedom is: 0\n"
]
}
],
"source": [
"# Fix the inlet conditions\n",
"\n",
"# Benzene stream\n",
"m.fs.mixer_1.inlet_1.flow_mol.fix(100) # converting to mol/s as unit basis is mol/s\n",
"m.fs.mixer_1.inlet_1.mole_frac_comp[0, \"benzene\"].fix(1)\n",
"m.fs.mixer_1.inlet_1.mole_frac_comp[0, \"toluene\"].fix(0)\n",
"m.fs.mixer_1.inlet_1.pressure.fix(101325) # Pa\n",
"m.fs.mixer_1.inlet_1.temperature.fix(353) # K\n",
"\n",
"# Toluene stream\n",
"m.fs.mixer_1.inlet_2.flow_mol.fix(100) # converting to mol/s as unit basis is mol/s\n",
"m.fs.mixer_1.inlet_2.mole_frac_comp[0, \"benzene\"].fix(0)\n",
"m.fs.mixer_1.inlet_2.mole_frac_comp[0, \"toluene\"].fix(1)\n",
"m.fs.mixer_1.inlet_2.pressure.fix(202650) # Pa\n",
"m.fs.mixer_1.inlet_2.temperature.fix(356) # K\n",
"\n",
"# Call the degrees_of_freedom function, get final DOF\n",
"DOF_final = degrees_of_freedom(m)\n",
"print(\"The final degrees of freedom is: {0}\".format(DOF_final))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Flowsheet Initialization"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"# Initialize the flowsheet, and set the output at WARNING\n",
"m.fs.mixer_1.initialize(outlvl=idaeslog.WARNING)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Obtaining Simulation Results"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Ipopt 3.13.2: \n",
"\n",
"******************************************************************************\n",
"This program contains Ipopt, a library for large-scale nonlinear optimization.\n",
" Ipopt is released as open source code under the Eclipse Public License (EPL).\n",
" For more information visit http://projects.coin-or.org/Ipopt\n",
"\n",
"This version of Ipopt was compiled from source code available at\n",
" https://github.com/IDAES/Ipopt as part of the Institute for the Design of\n",
" Advanced Energy Systems Process Systems Engineering Framework (IDAES PSE\n",
" Framework) Copyright (c) 2018-2019. See https://github.com/IDAES/idaes-pse.\n",
"\n",
"This version of Ipopt was compiled using HSL, a collection of Fortran codes\n",
" for large-scale scientific computation. All technical papers, sales and\n",
" publicity material resulting from use of the HSL codes within IPOPT must\n",
" contain the following acknowledgement:\n",
" HSL, a collection of Fortran codes for large-scale scientific\n",
" computation. See http://www.hsl.rl.ac.uk.\n",
"******************************************************************************\n",
"\n",
"This is Ipopt version 3.13.2, running with linear solver ma27.\n",
"\n",
"Number of nonzeros in equality constraint Jacobian...: 68\n",
"Number of nonzeros in inequality constraint Jacobian.: 0\n",
"Number of nonzeros in Lagrangian Hessian.............: 31\n",
"\n",
"Total number of variables............................: 25\n",
" variables with only lower bounds: 3\n",
" variables with lower and upper bounds: 8\n",
" variables with only upper bounds: 0\n",
"Total number of equality constraints.................: 25\n",
"Total number of inequality constraints...............: 0\n",
" inequality constraints with only lower bounds: 0\n",
" inequality constraints with lower and upper bounds: 0\n",
" inequality constraints with only upper bounds: 0\n",
"\n",
"iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n",
" 0 0.0000000e+00 3.58e+02 1.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
" 1 0.0000000e+00 3.58e+00 1.00e-02 -1.0 1.00e-02 - 9.90e-01 9.90e-01h 1\n",
" 2 0.0000000e+00 3.54e-02 1.98e-03 -1.0 1.00e-04 - 9.90e-01 9.90e-01h 1\n",
" 3 0.0000000e+00 9.31e-10 1.32e+01 -1.0 9.90e-07 - 1.00e+00 1.00e+00h 1\n",
"\n",
"Number of Iterations....: 3\n",
"\n",
" (scaled) (unscaled)\n",
"Objective...............: 0.0000000000000000e+00 0.0000000000000000e+00\n",
"Dual infeasibility......: 0.0000000000000000e+00 0.0000000000000000e+00\n",
"Constraint violation....: 2.0968859831870735e-12 9.3132257461547852e-10\n",
"Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
"Overall NLP error.......: 2.0968859831870735e-12 9.3132257461547852e-10\n",
"\n",
"\n",
"Number of objective function evaluations = 4\n",
"Number of objective gradient evaluations = 4\n",
"Number of equality constraint evaluations = 4\n",
"Number of inequality constraint evaluations = 0\n",
"Number of equality constraint Jacobian evaluations = 4\n",
"Number of inequality constraint Jacobian evaluations = 0\n",
"Number of Lagrangian Hessian evaluations = 3\n",
"Total CPU secs in IPOPT (w/o function evaluations) = 0.000\n",
"Total CPU secs in NLP function evaluations = 0.000\n",
"\n",
"EXIT: Optimal Solution Found.\n",
"\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
]
}
],
"source": [
"# Solve the simulation using ipopt\n",
"# Note: If the degrees of freedom = 0, we have a square problem\n",
"opt = SolverFactory(\"ipopt\")\n",
"result = opt.solve(m, tee=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### View Results"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"====================================================================================\n",
"Unit : fs.mixer_1 Time: 0.0\n",
"------------------------------------------------------------------------------------\n",
" Stream Table\n",
" Units inlet_1 inlet_2 Outlet \n",
" flow_mol mole / second 100 100 200.00\n",
" mole_frac_comp benzene dimensionless 1 0 0.50000\n",
" mole_frac_comp toluene dimensionless 0 1 0.50000\n",
" temperature kelvin 353 356 354.61\n",
" pressure pascal 101325 202650 1.0133e+05\n",
"====================================================================================\n"
]
}
],
"source": [
"# Display output report\n",
"m.fs.mixer_1.report()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Case 2\n",
"\n",
"For case 2, we will specify the inlet names for the two inlets, and set `momentum_mixing` type set to \"equality\" (in this case, pressure will be specified for only one inlet stream). We will name the 2 inlets as \"benzene_inlet\" and \"toluene_inlet\". "
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"# Create an instance of another mixer unit, attaching it to the same flowsheet.\n",
"# Specify that the property package to be used with the mixer is the one we created earlier,\n",
"# inlet list is specified but names are specified, and momentum mixing type is equality\n",
"\n",
"m.fs.mixer_2 = Mixer(\n",
" property_package=m.fs.properties,\n",
" inlet_list=[\"benzene_inlet\", \"toluene_inlet\"],\n",
" momentum_mixing_type=MomentumMixingType.equality,\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The initial degrees of freedom is: 9\n"
]
}
],
"source": [
"# Check the required degrees of freedom\n",
"DOF_init = degrees_of_freedom(m.fs.mixer_2)\n",
"print(\"The initial degrees of freedom is: {0}\".format(DOF_init))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We see that the degrees of freedom has dropped by 1 to 9 when compared with case 1. This is because we selected the `momentum_mixing_type` as `MomentumMixingType.equality` which basically adds a constraint that equates the pressure between all inlets and the outlet. Therefore, when we specify the inlet confitions in the next cell, we will define the pressure for only the `benzene_inlet` stream. "
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The final degrees of freedom is: 0\n"
]
}
],
"source": [
"# Fix the stream inlet conditions\n",
"\n",
"# Benzene stream\n",
"m.fs.mixer_2.benzene_inlet.flow_mol.fix(\n",
" 100\n",
") # converting to mol/s as unit basis is mol/s\n",
"m.fs.mixer_2.benzene_inlet.mole_frac_comp[0, \"benzene\"].fix(1)\n",
"m.fs.mixer_2.benzene_inlet.mole_frac_comp[0, \"toluene\"].fix(0)\n",
"m.fs.mixer_2.benzene_inlet.pressure.fix(\n",
" 101325\n",
") # Pa , Another option is m1.fs.mixer2.inlet2.pressure.fix(202650)\n",
"m.fs.mixer_2.benzene_inlet.temperature.fix(353) # K\n",
"\n",
"# Toluene stream\n",
"m.fs.mixer_2.toluene_inlet.flow_mol.fix(\n",
" 100\n",
") # converting to mol/s as unit basis is mol/s\n",
"m.fs.mixer_2.toluene_inlet.mole_frac_comp[0, \"benzene\"].fix(0)\n",
"m.fs.mixer_2.toluene_inlet.mole_frac_comp[0, \"toluene\"].fix(1)\n",
"m.fs.mixer_2.toluene_inlet.temperature.fix(356) # K\n",
"\n",
"DOF_final = degrees_of_freedom(m.fs.mixer_2)\n",
"print(\"The final degrees of freedom is: {0}\".format(DOF_final))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Flowsheet Initialization"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"# Initialize the flowsheet, and set the output at WARNING\n",
"\n",
"m.fs.mixer_2.initialize(outlvl=idaeslog.WARNING)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Obtaining Simulation Results"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Ipopt 3.13.2: \n",
"\n",
"******************************************************************************\n",
"This program contains Ipopt, a library for large-scale nonlinear optimization.\n",
" Ipopt is released as open source code under the Eclipse Public License (EPL).\n",
" For more information visit http://projects.coin-or.org/Ipopt\n",
"\n",
"This version of Ipopt was compiled from source code available at\n",
" https://github.com/IDAES/Ipopt as part of the Institute for the Design of\n",
" Advanced Energy Systems Process Systems Engineering Framework (IDAES PSE\n",
" Framework) Copyright (c) 2018-2019. See https://github.com/IDAES/idaes-pse.\n",
"\n",
"This version of Ipopt was compiled using HSL, a collection of Fortran codes\n",
" for large-scale scientific computation. All technical papers, sales and\n",
" publicity material resulting from use of the HSL codes within IPOPT must\n",
" contain the following acknowledgement:\n",
" HSL, a collection of Fortran codes for large-scale scientific\n",
" computation. See http://www.hsl.rl.ac.uk.\n",
"******************************************************************************\n",
"\n",
"This is Ipopt version 3.13.2, running with linear solver ma27.\n",
"\n",
"Number of nonzeros in equality constraint Jacobian...: 66\n",
"Number of nonzeros in inequality constraint Jacobian.: 0\n",
"Number of nonzeros in Lagrangian Hessian.............: 30\n",
"\n",
"Total number of variables............................: 24\n",
" variables with only lower bounds: 4\n",
" variables with lower and upper bounds: 8\n",
" variables with only upper bounds: 0\n",
"Total number of equality constraints.................: 24\n",
"Total number of inequality constraints...............: 0\n",
" inequality constraints with only lower bounds: 0\n",
" inequality constraints with lower and upper bounds: 0\n",
" inequality constraints with only upper bounds: 0\n",
"\n",
"iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n",
" 0 0.0000000e+00 3.58e+02 1.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
" 1 0.0000000e+00 3.58e+00 1.00e-02 -1.0 1.00e-02 - 9.90e-01 9.90e-01h 1\n",
" 2 0.0000000e+00 3.54e-02 1.98e-03 -1.0 1.00e-04 - 9.90e-01 9.90e-01h 1\n",
" 3 0.0000000e+00 9.31e-10 1.32e+01 -1.0 9.90e-07 - 1.00e+00 1.00e+00h 1\n",
"\n",
"Number of Iterations....: 3\n",
"\n",
" (scaled) (unscaled)\n",
"Objective...............: 0.0000000000000000e+00 0.0000000000000000e+00\n",
"Dual infeasibility......: 0.0000000000000000e+00 0.0000000000000000e+00\n",
"Constraint violation....: 2.0968859831870735e-12 9.3132257461547852e-10\n",
"Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
"Overall NLP error.......: 2.0968859831870735e-12 9.3132257461547852e-10\n",
"\n",
"\n",
"Number of objective function evaluations = 4\n",
"Number of objective gradient evaluations = 4\n",
"Number of equality constraint evaluations = 4\n",
"Number of inequality constraint evaluations = 0\n",
"Number of equality constraint Jacobian evaluations = 4\n",
"Number of inequality constraint Jacobian evaluations = 0\n",
"Number of Lagrangian Hessian evaluations = 3\n",
"Total CPU secs in IPOPT (w/o function evaluations) = 0.001\n",
"Total CPU secs in NLP function evaluations = 0.000\n",
"\n",
"EXIT: Optimal Solution Found.\n",
"\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
]
}
],
"source": [
"# Solve the simulation using ipopt\n",
"# Note: If the degrees of freedom = 0, we have a square problem\n",
"opt = SolverFactory(\"ipopt\")\n",
"result = opt.solve(m.fs.mixer_2, tee=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### View Results"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"====================================================================================\n",
"Unit : fs.mixer_2 Time: 0.0\n",
"------------------------------------------------------------------------------------\n",
" Stream Table\n",
" Units benzene_inlet toluene_inlet Outlet \n",
" flow_mol mole / second 100 100.00 200.00\n",
" mole_frac_comp benzene dimensionless 1 0.0000 0.50000\n",
" mole_frac_comp toluene dimensionless 0 1.0000 0.50000\n",
" temperature kelvin 353 356.00 354.61\n",
" pressure pascal 101325 1.0132e+05 1.0132e+05\n",
"====================================================================================\n"
]
}
],
"source": [
"# Display a readable report\n",
"m.fs.mixer_2.report()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"celltoolbar": "Tags",
"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.11.5"
}
},
"nbformat": 4,
"nbformat_minor": 3
}