\n",
+ " | Callback to SVD method of choice (default = svd_dense). Callbacks\n",
+ " | should take the Jacobian and number of singular values to compute as\n",
+ " | options, plus any method specific arguments, and should return the u,\n",
+ " | s and v matrices as numpy arrays.\n",
+ " | \n",
+ " | svd_callback_arguments: dict, optional\n",
+ " | Optional arguments to pass to SVD callback (default = None)\n",
+ " | \n",
+ " | singular_value_tolerance: float, default=1e-06\n",
+ " | Tolerance for defining a small singular value\n",
+ " | \n",
+ " | size_cutoff_in_singular_vector: float, default=0.1\n",
+ " | Size below which to ignore constraints and variables in the singular\n",
+ " | vector\n",
+ " | \n",
+ " | report_numerical_issues(self, stream=None)\n",
+ " | Generates a summary report of any numerical issues identified in the model provided\n",
+ " | and suggest next steps for debugging model.\n",
+ " | \n",
+ " | Numerical checks should only be performed once all structural issues have been resolved,\n",
+ " | and require that at least a partial solution to the model is available.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: I/O object to write report to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | report_structural_issues(self, stream=None)\n",
+ " | Generates a summary report of any structural issues identified in the model provided\n",
+ " | and suggests next steps for debugging the model.\n",
+ " | \n",
+ " | This should be the first method called when debugging a model and after any change\n",
+ " | is made to the model. These checks can be run before trying to initialize and solve\n",
+ " | the model.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: I/O object to write report to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | ----------------------------------------------------------------------\n",
+ " | Readonly properties defined here:\n",
+ " | \n",
+ " | model\n",
+ " | Model currently being diagnosed.\n",
+ " | \n",
+ " | ----------------------------------------------------------------------\n",
+ " | Data descriptors defined here:\n",
+ " | \n",
+ " | __dict__\n",
+ " | dictionary for instance variables (if defined)\n",
+ " | \n",
+ " | __weakref__\n",
+ " | list of weak references to the object (if defined)\n",
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "help(DiagnosticsToolbox)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The ``help()`` function gives us a lot of information on the ``DiagnosticsToolbox`` and all the methods that it supports (and there are many). However, the important part to start with are the four steps outlined at the top of the doc string that tell us how to get started.\n",
+ "\n",
+ "Firstly, we need a model to test (and, for this tutorial at least, one that has a wide range of issues that we need to fix before it will solve). We then also need to fix some variables so that we have 0 degrees of freedom in our model. Whilst our ultimate goal is generally optimization (and thus a system with 1 or more degrees of freedom), all models conceptually derive from a square model representing a nominal state. If this nominal state is not well-posed, then any issues present will also be present in the resulting optimization (even if adding degrees of freedom means that the model is now easier to solve).\n",
+ "\n",
+ "The cell below contains a demonstration model for this tutorial that contains a number of issues that we will resolve using the ``DiagnosticsToolbox``."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import pyomo.environ as pyo\n",
+ "\n",
+ "m = pyo.ConcreteModel()\n",
+ "\n",
+ "m.v1 = pyo.Var(units=pyo.units.m)\n",
+ "m.v2 = pyo.Var(units=pyo.units.m)\n",
+ "m.v3 = pyo.Var(bounds=(0, 5))\n",
+ "m.v4 = pyo.Var()\n",
+ "m.v5 = pyo.Var(bounds=(0, 10))\n",
+ "m.v6 = pyo.Var()\n",
+ "m.v7 = pyo.Var(\n",
+ " units=pyo.units.m, bounds=(0, 1)\n",
+ ") # Poorly scaled variable with lower bound\n",
+ "m.v8 = pyo.Var() # unused variable\n",
+ "\n",
+ "m.c1 = pyo.Constraint(expr=m.v1 + m.v2 == 10) # Unit consistency issue\n",
+ "m.c2 = pyo.Constraint(expr=m.v3 == m.v4 + m.v5)\n",
+ "m.c3 = pyo.Constraint(expr=2 * m.v3 == 3 * m.v4 + 4 * m.v5 + m.v6)\n",
+ "m.c4 = pyo.Constraint(expr=m.v7 == 1e-8 * m.v1) # Poorly scaled constraint\n",
+ "\n",
+ "m.v4.fix(2)\n",
+ "m.v5.fix(2)\n",
+ "m.v6.fix(0)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Next, the instructions tell us to create an instance of the ``DiagnosticsToolbox`` and to pass the model we wish to examine as an argument.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Create an instance of the DiagnosticsToolbox: dt = DiagnosticsToolbox(m)\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "dt = DiagnosticsToolbox(m)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Finally, the instructions tell us to run the ``report_structural_issues()`` method. Structural issues represent issues that exist solely in the form of the model equations and thus do not depend on the current value of any of the variables. This is useful as it means we can check for these before we even call a solver, which can be critical as sometimes these issues will cause a solver to fail without providing a useful solution.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Call dt.report_structural_issues() in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The toolbox is telling us that the constraint which failed to converge is ``c2``, however this is generally only part of the story. Solvers work by trying to minimize the infeasibility in the model (residual of the constraints), which generally means they push any infeasibility onto the least sensitive constraint in the problem. Thus, the constraint which shows the infeasibility is often not the root cause of the problem, but only the symptom of the underlying issue.\n",
- "\n",
- "If we look back at the constraints, we can see that the same variables also appear in ``c3`` and that some of these have bounds, all of which could be contributing to the infeasibility. In this case the solver tried to minimize the residual in all the constraints and ended up pushing all the issues off onto ``c2``.\n",
- "\n",
- "\n",
- "Warning:\n",
- "When dealing with solver issues such as this, you should always remember that the obvious symptoms are often just the tip of the iceberg and that the real issue generally lies somewhere else; the challenge is tracing the symptoms back to their ultimate source.\n",
- "
\n",
- "\n",
- "Next, let us take a look at the variables at or outside their bounds as well. When a solver reports an potentially infeasible solution, the most common cause is unexpected bounds violations so you should always check these first.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Display the variables with bounds violations.\n",
- "
"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Activated Blocks: 1 (Deactivated: 0)\n",
+ " Free Variables in Activated Constraints: 4 (External: 0)\n",
+ " Free Variables with only lower bounds: 0\n",
+ " Free Variables with only upper bounds: 0\n",
+ " Free Variables with upper and lower bounds: 2\n",
+ " Fixed Variables in Activated Constraints: 3 (External: 0)\n",
+ " Activated Equality Constraints: 4 (Deactivated: 0)\n",
+ " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
+ " Activated Objectives: 0 (Deactivated: 0)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 WARNINGS\n",
+ "\n",
+ " WARNING: 1 Component with inconsistent units\n",
+ " WARNING: Structural singularity found\n",
+ " Under-Constrained Set: 3 variables, 2 constraints\n",
+ " Over-Constrained Set: 1 variables, 2 constraints\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 Cautions\n",
+ "\n",
+ " Caution: 1 variable fixed to 0\n",
+ " Caution: 1 unused variable (0 fixed)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " display_components_with_inconsistent_units()\n",
+ " display_underconstrained_set()\n",
+ " display_overconstrained_set()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Looking at the output from the ``report_structural_issues()`` method, we can see that it provides a fairly short summary containing 4 sections.\n",
+ "\n",
+ "1. The first section is a summary of the size of the model, indicating things like the number of variables and constraints. The size of the model is often important for judging how difficult it will be to solve, and this information can also be useful for comparison to what is being sent to the solver. Most solvers will report the size of the model in their output logs, and if there is a difference between what is reported here and by the solver, then you should probably look into what is happening. This section also notes some things such as if you have any deactivated Blocks, Constraints or Objectives, or if you have variables which appear in the constraints that are not part of the model; these are not necessarily wrong but it is easy to have accidentally deactivated something you did not intend to so you should always check to see that these are expected.\n",
+ "\n",
+ "2. The second section provides a summary of any critical structural issues that were found - in this case we can see that there are 2 warnings we are going to need to look into. Warnings represent issues that need to be addressed before moving on as these will likely cause the solver to fail or give an incorrect answer.\n",
+ "\n",
+ "3. The third section lists a summary of any cautions that are found. Cautions represent issues that may or may not be problematic; in many cases these might be expected behaviors or borderline issues. However, these could also represent conceptual issues that should be addressed, so users should take the time to investigate these and determine if they need to be fixed or not.\n",
+ "\n",
+ "4. Finally, there is a section that suggests the next steps to take to help guide you through the model diagnosis process. If any warnings were identified, this section will list methods that can help you get more information on each specific problem, and if no warnings are found then it will guide you onto the next step in the model diagnosis workflow.\n",
+ "\n",
+ "**Note:** there are methods available to help investigate cautions as well, but these will not show up in the next steps in order to avoid cluttering the output. You can get more information on the available methods for investigating cautions via the documentation or ``help()`` function.\n",
+ "\n",
+ "In our current model, we have 2 critical issues (warnings) that we need to look into and resolve. The order in which we resolve these will generally not matter, but be aware that these can often be interrelated - fixing one warning might resolve other warnings as well (or create new ones), and sometimes you will need to look at multiple issues together to find the overall root cause.\n",
+ "\n",
+ "To start with, let us look at the unit consistency issue. From the \"Next Steps\" section above, the toolbox is suggesting we run the ``display_components_with_inconsistent_units()`` method for more information.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Call the `display_components_with_inconsistent_units()` method from the DiagnosticsToolbox to see more information on which constraint is causing the unit consistency issues.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {
+ "scrolled": false,
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "code",
- "execution_count": 30,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "The following variable(s) have values at or outside their bounds (tol=0.0E+00):\n",
- "\n",
- " v3 (free): value=0.0 bounds=(0, 5)\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.display_variables_at_or_outside_bounds()"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "The following component(s) have unit consistency issues:\n",
+ "\n",
+ " c1\n",
+ "\n",
+ "For more details on unit inconsistencies, import the assert_units_consistent method\n",
+ "from pyomo.util.check_units\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.display_components_with_inconsistent_units()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This tells us that the issue lies in constraint ``c1``. If we go back and look at this constraint, we can see that it says ``v1 + v2 == 10``. ``v1`` and ``v2`` both have units of ``m`` which is consistent, but the constant in the expression (right hand side) is unitless. Thus, we need to correct this so that the right hand side has units for the constraint to be consistent.\n",
+ "\n",
+ "The cell below shows how to delete a constraint and replace it with a new one with the correct units.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "Deleting components can cause unexpected issues if something else in a model is using that component (e.g., deleting a variable which is used in a constraint). You should always be careful when deleting Pyomo components and make sure you only delete components that are not used elsewhere.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Delete the incorrect Constraint\n",
+ "m.del_component(m.c1)\n",
+ "\n",
+ "# Re-create the Constraint with the correct units\n",
+ "m.c1 = pyo.Constraint(expr=m.v1 + m.v2 == 10 * pyo.units.m)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Warning:\n",
+ "Fixing issues in models is often an iterative process requiring trial and error. You might also have some results from a model before running the diagnostics tools and the changes you make during debugging may make it difficult to replicate those results afterwards.\n",
+ " \n",
+ "It is strongly recommended that you keep a record of the changes you make at each step and why, along with a Git hash (or similar version control marker) corresponding to these changes. This will allow you see what changes and why, and give you a way to go back to previous iterations if the current approach does not work out. The IDAES documentation contains recommendations on how to keep and maintain a modeling logbook.\n",
+ "
\n",
+ "\n",
+ "Now, re-run the ``report_structural_issues()`` method and see if this change has fixed the unit consistency issue.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Call dt.report_structural_issues() in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {
+ "scrolled": true,
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The toolbox is telling us that ``v3`` is the variable with a potential issue. It is also showing us the current value and bounds for ``v3`` as well as if it is a fixed or free variable, which will be useful for diagnosing the issues.\n",
- "\n",
- "We can see that ``v3`` is a free variable with bounds between 0 and 5 and a current value of 0. As ``v3`` is a free variable, this suggests that the solver has pushed the value to the bound where it cannot go any further, and this might be part of the cause of our infeasibility.\n",
- "\n",
- "\n",
- "Warning:\n",
- "When dealing with bounds violations you should always start by understanding why the bounds exist and what they mean - in many cases a bound indicates the range over which the model can be trusted and that going beyond this may result in unexpected behavior due to extrapolation.\n",
- " \n",
- "Never arbitrarily change a bound just because it is causing your model to be infeasible without understanding the consequences of this decision. Often, a bound violation is an indication that you need to re-think some of the constraints in your model to find alternatives which are valid in the actual range of values you are trying to solve for.\n",
- "
\n",
- "\n",
- "For this example, let us assume that we made a mistake with the bounds on ``v3`` and set the lower bound to be -5.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Update the bounds on v3 in the cell below.\n",
- "
"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Activated Blocks: 1 (Deactivated: 0)\n",
+ " Free Variables in Activated Constraints: 4 (External: 0)\n",
+ " Free Variables with only lower bounds: 0\n",
+ " Free Variables with only upper bounds: 0\n",
+ " Free Variables with upper and lower bounds: 2\n",
+ " Fixed Variables in Activated Constraints: 3 (External: 0)\n",
+ " Activated Equality Constraints: 4 (Deactivated: 0)\n",
+ " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
+ " Activated Objectives: 0 (Deactivated: 0)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "1 WARNINGS\n",
+ "\n",
+ " WARNING: Structural singularity found\n",
+ " Under-Constrained Set: 3 variables, 2 constraints\n",
+ " Over-Constrained Set: 1 variables, 2 constraints\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 Cautions\n",
+ "\n",
+ " Caution: 1 variable fixed to 0\n",
+ " Caution: 1 unused variable (0 fixed)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " display_underconstrained_set()\n",
+ " display_overconstrained_set()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The unit consistency issue has been resolved by the changes above, so now we need to look at the structural singularity. A structural singularity occurs when one sub-part of the model is over-constrained (negative degrees of freedom), which generally means another part is under-constrained (positive degrees of freedom, assuming that there are 0 degrees of freedom overall).\n",
+ "\n",
+ "The toolbox is suggesting we use the ``display_overconstrained_set()`` and ``display_underconstrained_set()`` methods to get more information on the singularity; for now, let us start with the over-constrained set.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Call dt.display_overconstrained_set() in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "code",
- "execution_count": 32,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "m.v3.setlb(-5)"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Dulmage-Mendelsohn Over-Constrained Set\n",
+ "\n",
+ " Independent Block 0:\n",
+ "\n",
+ " Variables:\n",
+ "\n",
+ " v3\n",
+ "\n",
+ " Constraints:\n",
+ "\n",
+ " c2\n",
+ " c3\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.display_overconstrained_set()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "From the output above, the toolbox is telling us that we have two constraints (``c2`` and ``c3``) which only contain a single unfixed variable (``v3``); thus in this part of the model we have -1 degree of freedom and the model is not well defined (structurally singular). If we go back and look at these constraints, we can see the that the constraints are:\n",
+ "\n",
+ "``c2: v3 == v4 + v5``\n",
+ "\n",
+ "``c3: 2*v3 == 3*v4 + 4*v5 + v6``\n",
+ "\n",
+ "We can see that in addition to ``v3`` these constraints actually contain 3 other variables (``v4``, ``v5`` and ``v6``), however these are all variables we fixed to get our initial zero degrees of freedom. It looks like we have either accidentally fixed one too many variables or written one too many constraints.\n",
+ "\n",
+ "For this example, let us assume that ``v4`` was not supposed to be fixed and unfix it.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Resolve the structural singularity and then call dt.report_structural_issues() in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "metadata": {
+ "scrolled": true,
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Now that we have fixed the bounds issues, we should check whether our model is now feasible. However, before we continue we should recognize that we have just made a structural change to the model. If we were not careful, this could have introduced new structural issues to the model, so we should start from the beginning just to be sure.\n",
- "\n",
- "\n",
- "Warning:\n",
- "In general, you should always start from the beginning of the model diagnosis workflow after you make any change to the model. Remember to also record these changes in your log book in case something unexpected happens so that you can revert any changes that cause problems.\n",
- "
\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Check to see if there are any new structural issues in the cell below.\n",
- "
"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Activated Blocks: 1 (Deactivated: 0)\n",
+ " Free Variables in Activated Constraints: 5 (External: 0)\n",
+ " Free Variables with only lower bounds: 0\n",
+ " Free Variables with only upper bounds: 0\n",
+ " Free Variables with upper and lower bounds: 2\n",
+ " Fixed Variables in Activated Constraints: 2 (External: 0)\n",
+ " Activated Equality Constraints: 4 (Deactivated: 0)\n",
+ " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
+ " Activated Objectives: 0 (Deactivated: 0)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 WARNINGS\n",
+ "\n",
+ " WARNING: 1 Degree of Freedom\n",
+ " WARNING: Structural singularity found\n",
+ " Under-Constrained Set: 3 variables, 2 constraints\n",
+ " Over-Constrained Set: 0 variables, 0 constraints\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 Cautions\n",
+ "\n",
+ " Caution: 1 variable fixed to 0\n",
+ " Caution: 1 unused variable (0 fixed)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " display_underconstrained_set()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "m.v4.unfix()\n",
+ "\n",
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We can see that the over-constrained set is now empty (0 variables and 0 constraints) but the under-constrained set still has 3 variables and only 2 constraints. We can also see that there is a new warning about having 1 degree of freedom in the model, however this should not be surprising as we have just unfixed ``v4`` to resolve the over-constrained set so we have added a degree of freedom to the model.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Display the under-constrained set in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 20,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "code",
- "execution_count": 34,
- "metadata": {
- "scrolled": true,
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Model Statistics\n",
- "\n",
- " Activated Blocks: 1 (Deactivated: 0)\n",
- " Free Variables in Activated Constraints: 4 (External: 0)\n",
- " Free Variables with only lower bounds: 0\n",
- " Free Variables with only upper bounds: 0\n",
- " Free Variables with upper and lower bounds: 2\n",
- " Fixed Variables in Activated Constraints: 3 (External: 0)\n",
- " Activated Equality Constraints: 4 (Deactivated: 0)\n",
- " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
- " Activated Objectives: 0 (Deactivated: 0)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "0 WARNINGS\n",
- "\n",
- " No warnings found!\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "2 Cautions\n",
- "\n",
- " Caution: 1 variable fixed to 0\n",
- " Caution: 1 unused variable (0 fixed)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "Suggested next steps:\n",
- "\n",
- " Try to initialize/solve your model and then call report_numerical_issues()\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.report_structural_issues()"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Dulmage-Mendelsohn Under-Constrained Set\n",
+ "\n",
+ " Independent Block 0:\n",
+ "\n",
+ " Variables:\n",
+ "\n",
+ " v2\n",
+ " v1\n",
+ " v7\n",
+ "\n",
+ " Constraints:\n",
+ "\n",
+ " c1\n",
+ " c4\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.display_underconstrained_set()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Looking at the output from the ``display_underconstrained_set()`` method, we can see that we have two constraints, ``c1`` and ``c4``, which contain three unfixed variables, ``v1``, ``v2`` and ``v7``. Thus, we have one degree of freedom that needs to be addressed. To fix this, we could either fix one of the variables shown or add an additional equality constraint to the model.\n",
+ "\n",
+ "For this example let's fix ``v2`` to a value of 5 and then re-run the ``report_structural_issues()`` method.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Fix v2 to a value of 5 and then re-run dt.report_structural_issues.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "metadata": {
+ "scrolled": true,
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Our change has not introduced any new structural issues, so we can move on and try to solve the model again.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Re-solve the model in the cell below.\n",
- "
"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Activated Blocks: 1 (Deactivated: 0)\n",
+ " Free Variables in Activated Constraints: 4 (External: 0)\n",
+ " Free Variables with only lower bounds: 0\n",
+ " Free Variables with only upper bounds: 0\n",
+ " Free Variables with upper and lower bounds: 2\n",
+ " Fixed Variables in Activated Constraints: 3 (External: 0)\n",
+ " Activated Equality Constraints: 4 (Deactivated: 0)\n",
+ " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
+ " Activated Objectives: 0 (Deactivated: 0)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "0 WARNINGS\n",
+ "\n",
+ " No warnings found!\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 Cautions\n",
+ "\n",
+ " Caution: 1 variable fixed to 0\n",
+ " Caution: 1 unused variable (0 fixed)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " Try to initialize/solve your model and then call report_numerical_issues()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "m.v2.fix(5)\n",
+ "\n",
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The toolbox is now telling us that no warnings were found, so we have resolved all the structural issues (for now at least). The toolbox is telling us that there are also 2 non-critical issues (cautions) that we should look at; one about an unused variable and one about a variable fixed to zero. If you wish, you can look into identifying and fixing these yourself, however for this example we will move on to the next step (remember that the toolbox has methods to display more details for each of these which you can find in the documentation or from the ``help()`` function).\n",
+ "\n",
+ "For the Next Steps section, the toolbox is recommending we try to solve our model and then check for numerical issues.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Use the Pyomo SolverFactory to create an instance of IPOPT and then try to solve the model. Make sure to set \"tee=True\" as this is going to fail (and it is always good practice to review the solver logs).\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "code",
- "execution_count": 36,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "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...: 7\n",
- "Number of nonzeros in inequality constraint Jacobian.: 0\n",
- "Number of nonzeros in Lagrangian Hessian.............: 0\n",
- "\n",
- "Total number of variables............................: 4\n",
- " variables with only lower bounds: 0\n",
- " variables with lower and upper bounds: 2\n",
- " variables with only upper bounds: 0\n",
- "Total number of equality constraints.................: 4\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 6.67e-01 0.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
- " 1 0.0000000e+00 6.66e-03 2.97e+00 -1.0 2.00e+00 - 7.17e-01 9.90e-01h 1\n",
- " 2 0.0000000e+00 6.27e-05 9.38e+00 -1.0 2.00e-02 - 1.00e+00 9.91e-01h 1\n",
- " 3 0.0000000e+00 8.88e-16 1.13e-12 -1.0 1.88e-04 - 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....: 8.8817841970012523e-16 8.8817841970012523e-16\n",
- "Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
- "Overall NLP error.......: 8.8817841970012523e-16 8.8817841970012523e-16\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"
- ]
- },
- {
- "data": {
- "text/plain": [
- "{'Problem': [{'Lower bound': -inf, 'Upper bound': inf, 'Number of objectives': 1, 'Number of constraints': 4, 'Number of variables': 4, 'Sense': 'unknown'}], 'Solver': [{'Status': 'ok', 'Message': 'Ipopt 3.13.2\\\\x3a Optimal Solution Found', 'Termination condition': 'optimal', 'Id': 0, 'Error rc': 0, 'Time': 0.02317023277282715}], 'Solution': [OrderedDict([('number of solutions', 0), ('number of solutions displayed', 0)])]}"
- ]
- },
- "execution_count": 36,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "solver.solve(m, tee=True)"
- ]
+ "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...: 7\n",
+ "Number of nonzeros in inequality constraint Jacobian.: 0\n",
+ "Number of nonzeros in Lagrangian Hessian.............: 0\n",
+ "\n",
+ "Total number of variables............................: 4\n",
+ " variables with only lower bounds: 0\n",
+ " variables with lower and upper bounds: 2\n",
+ " variables with only upper bounds: 0\n",
+ "Total number of equality constraints.................: 4\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 1.40e+01 0.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
+ " 1 0.0000000e+00 1.39e+01 1.50e+02 -1.0 6.00e+00 - 7.16e-01 4.93e-03h 1\n",
+ " 2 0.0000000e+00 1.39e+01 3.03e+06 -1.0 5.97e+00 - 1.00e+00 4.95e-05h 1\n",
+ " 3r 0.0000000e+00 1.39e+01 1.00e+03 1.1 0.00e+00 - 0.00e+00 2.47e-07R 2\n",
+ " 4r 0.0000000e+00 4.19e+00 9.42e+02 1.1 3.50e+03 - 4.02e-01 3.37e-03f 1\n",
+ " 5r 0.0000000e+00 2.12e+00 8.72e+02 1.1 5.89e+01 - 4.35e-01 7.06e-02f 1\n",
+ " 6r 0.0000000e+00 6.74e-01 6.06e+02 1.1 5.29e+00 - 9.93e-03 3.98e-01f 1\n",
+ " 7r 0.0000000e+00 6.80e-01 3.14e+02 0.4 2.05e-01 - 1.00e+00 1.03e-01f 1\n",
+ " 8r 0.0000000e+00 6.69e-01 2.78e-05 0.4 2.58e-02 - 1.00e+00 1.00e+00f 1\n",
+ " 9r 0.0000000e+00 6.67e-01 7.56e+00 -1.7 8.13e-03 - 9.93e-01 9.96e-01f 1\n",
+ "iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n",
+ " 10r 0.0000000e+00 6.67e-01 2.23e-07 -1.7 4.13e-05 - 1.00e+00 1.00e+00f 1\n",
+ " 11r 0.0000000e+00 6.67e-01 6.73e-01 -3.7 6.61e-05 - 1.00e+00 1.00e+00f 1\n",
+ " 12r 0.0000000e+00 6.67e-01 1.91e-09 -3.7 1.48e-09 - 1.00e+00 1.00e+00h 1\n",
+ " 13r 0.0000000e+00 6.67e-01 2.69e+00 -8.4 5.74e-07 - 1.00e+00 9.26e-01f 1\n",
+ " 14r 0.0000000e+00 6.67e-01 7.65e+01 -8.4 4.23e-08 - 8.68e-01 1.00e+00f 1\n",
+ "\n",
+ "Number of Iterations....: 14\n",
+ "\n",
+ " (scaled) (unscaled)\n",
+ "Objective...............: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Dual infeasibility......: 3.2644919411246030e-04 3.2644919411246030e-04\n",
+ "Constraint violation....: 6.6666666333656233e-01 6.6666666333656233e-01\n",
+ "Complementarity.........: 4.6615546565561981e-09 4.6615546565561981e-09\n",
+ "Overall NLP error.......: 6.6666666333656233e-01 6.6666666333656233e-01\n",
+ "\n",
+ "\n",
+ "Number of objective function evaluations = 18\n",
+ "Number of objective gradient evaluations = 5\n",
+ "Number of equality constraint evaluations = 18\n",
+ "Number of inequality constraint evaluations = 0\n",
+ "Number of equality constraint Jacobian evaluations = 17\n",
+ "Number of inequality constraint Jacobian evaluations = 0\n",
+ "Number of Lagrangian Hessian evaluations = 15\n",
+ "Total CPU secs in IPOPT (w/o function evaluations) = 0.003\n",
+ "Total CPU secs in NLP function evaluations = 0.000\n",
+ "\n",
+ "EXIT: Converged to a point of local infeasibility. Problem may be infeasible.\n",
+ "WARNING: Loading a SolverResults object with a warning status into\n",
+ "model.name=\"unknown\";\n",
+ " - termination condition: infeasible\n",
+ " - message from solver: Ipopt 3.13.2\\x3a Converged to a locally infeasible\n",
+ " point. Problem may be infeasible.\n"
+ ]
},
{
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "IPOPT should have returned optimal solution now, so it looks like those bounds were what was causing the model to be infeasible. At this point, the model is now solving (for the current values at least), so you might think that the model is now ready for optimization.\n",
- "\n",
- "However, if we look at the solver logs we can see that it took around 3 iterations for IPOPT to solve our model (depending on minor variations in computer architecture). For a model this simple, we would generally expect it to solve in only 1 iteration so there is still some room for improvement.\n",
- "\n",
- "\n",
- "Warning:\n",
- "You should keep in mind that just because you get an optimal solution does not mean that your model is robust and free of issues.\n",
- " \n",
- "You should always take the time to look over the solver logs to look for signs of trouble, even if you get an optimal solution. While you might get an optimal solution for the current state, there may be advance warning signs of issues that will cause problems later when you try to solve the model at a different state.\n",
- "
\n",
- "\n",
- "Let us run the ``report_numerical_issues`` method again to see if there are any other problems we need to address.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Check for additional numerical issues in the cell below.\n",
- "
"
+ "data": {
+ "text/plain": [
+ "{'Problem': [{'Lower bound': -inf, 'Upper bound': inf, 'Number of objectives': 1, 'Number of constraints': 4, 'Number of variables': 4, 'Sense': 'unknown'}], 'Solver': [{'Status': 'warning', 'Message': 'Ipopt 3.13.2\\\\x3a Converged to a locally infeasible point. Problem may be infeasible.', 'Termination condition': 'infeasible', 'Id': 200, 'Error rc': 0, 'Time': 0.007064104080200195}], 'Solution': [OrderedDict([('number of solutions', 0), ('number of solutions displayed', 0)])]}"
]
- },
+ },
+ "execution_count": 24,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "solver = pyo.SolverFactory(\"ipopt\")\n",
+ "solver.solve(m, tee=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As hinted at above, IPOPT has returned a warning that the problem may be infeasible. Before moving on however, it is always good practice to look over the solver outputs and see what it is telling you.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "A lot of useful information is contained in the solver logs which is extremely useful when diagnosing modeling issues. Each solver has its own way of reporting output and its own specific behavior, so you will need to learn to interpret the output of each solver you use. The IDAES Documentation contains some guidance on interpreting output logs for a few common solvers.\n",
+ "
\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Call the report_numerical_issues method in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 26,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "code",
- "execution_count": 38,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Model Statistics\n",
- "\n",
- " Jacobian Condition Number: 1.700E+01\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "0 WARNINGS\n",
- "\n",
- " No warnings found!\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "5 Cautions\n",
- "\n",
- " Caution: 1 Variable with value close to their bounds (abs=1.0E-04, rel=1.0E-04)\n",
- " Caution: 1 Variable with value close to zero (tol=1.0E-08)\n",
- " Caution: 1 Variable with extreme value (<1.0E-04 or >1.0E+04)\n",
- " Caution: 1 Variable with None value\n",
- " Caution: 1 extreme Jacobian Entry (<1.0E-04 or >1.0E+04)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "Suggested next steps:\n",
- "\n",
- " If you still have issues converging your model consider:\n",
- " prepare_svd_toolbox()\n",
- " prepare_degeneracy_hunter()\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.report_numerical_issues()"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Jacobian Condition Number: 1.700E+01\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 WARNINGS\n",
+ "\n",
+ " WARNING: 1 Constraint with large residuals (>1.0E-05)\n",
+ " WARNING: 1 Variable at or outside bounds (tol=0.0E+00)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "5 Cautions\n",
+ "\n",
+ " Caution: 2 Variables with value close to their bounds (abs=1.0E-04, rel=1.0E-04)\n",
+ " Caution: 2 Variables with value close to zero (tol=1.0E-08)\n",
+ " Caution: 1 Variable with extreme value (<1.0E-04 or >1.0E+04)\n",
+ " Caution: 1 Variable with None value\n",
+ " Caution: 1 extreme Jacobian Entry (<1.0E-04 or >1.0E+04)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " display_constraints_with_large_residuals()\n",
+ " display_variables_at_or_outside_bounds()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.report_numerical_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The ``report_numerical_issues()`` provides a summary similar to that which we saw for the structural issues. Firstly, it reports to us the Jacobian condition number for our problem which can give us an idea of how well-scaled the problem is, followed by a list of warnings, cautions and suggested next steps.\n",
+ "\n",
+ "Unsurprisingly, we are seeing a warning about a constraint with a large residual which we would expect when a solver reports a potentially infeasible problem. We are also seeing a warning about a variable with bound violations which might be contributing to the potential infeasibility.\n",
+ "\n",
+ "For the next steps, the toolbox is suggesting some new methods to get more information on these issues; let us start by looking at the constraints with large residuals.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Display the constraint with a large residual in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 28,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The toolbox is not reporting any warnings which is good, however there are still 5 numerical cautions that it has identified which might be contributing to the larger than expected number of iterations. As mentioned earlier, the toolbox does not suggest methods for investigating these, but there are methods available. For example, we can look at the variable with an extreme value using the `display_variables_with_extreme_values()` method.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Check for additional information about variables with extreme values.\n",
- "
"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "The following constraint(s) have large residuals (>1.0E-05):\n",
+ "\n",
+ " c2: 6.66667E-01\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.display_constraints_with_large_residuals()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The toolbox is telling us that the constraint which failed to converge is ``c2``, however this is generally only part of the story. Solvers work by trying to minimize the infeasibility in the model (residual of the constraints), which generally means they push any infeasibility onto the least sensitive constraint in the problem. Thus, the constraint which shows the infeasibility is often not the root cause of the problem, but only the symptom of the underlying issue.\n",
+ "\n",
+ "If we look back at the constraints, we can see that the same variables also appear in ``c3`` and that some of these have bounds, all of which could be contributing to the infeasibility. In this case the solver tried to minimize the residual in all the constraints and ended up pushing all the issues off onto ``c2``.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "When dealing with solver issues such as this, you should always remember that the obvious symptoms are often just the tip of the iceberg and that the real issue generally lies somewhere else; the challenge is tracing the symptoms back to their ultimate source.\n",
+ "
\n",
+ "\n",
+ "Next, let us take a look at the variables at or outside their bounds as well. When a solver reports an potentially infeasible solution, the most common cause is unexpected bounds violations so you should always check these first.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Display the variables with bounds violations.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 30,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "code",
- "execution_count": 40,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "The following variable(s) have extreme values (<1.0E-04 or > 1.0E+04):\n",
- "\n",
- " v7: 4.9999999999999945e-08\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.display_variables_with_extreme_values()"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "The following variable(s) have values at or outside their bounds (tol=0.0E+00):\n",
+ "\n",
+ " v3 (free): value=0.0 bounds=(0, 5)\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.display_variables_at_or_outside_bounds()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The toolbox is telling us that ``v3`` is the variable with a potential issue. It is also showing us the current value and bounds for ``v3`` as well as if it is a fixed or free variable, which will be useful for diagnosing the issues.\n",
+ "\n",
+ "We can see that ``v3`` is a free variable with bounds between 0 and 5 and a current value of 0. As ``v3`` is a free variable, this suggests that the solver has pushed the value to the bound where it cannot go any further, and this might be part of the cause of our infeasibility.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "When dealing with bounds violations you should always start by understanding why the bounds exist and what they mean - in many cases a bound indicates the range over which the model can be trusted and that going beyond this may result in unexpected behavior due to extrapolation.\n",
+ " \n",
+ "Never arbitrarily change a bound just because it is causing your model to be infeasible without understanding the consequences of this decision. Often, a bound violation is an indication that you need to re-think some of the constraints in your model to find alternatives which are valid in the actual range of values you are trying to solve for.\n",
+ "
\n",
+ "\n",
+ "For this example, let us assume that we made a mistake with the bounds on ``v3`` and set the lower bound to be -5.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Update the bounds on v3 in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 32,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "m.v3.setlb(-5)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now that we have fixed the bounds issues, we should check whether our model is now feasible. However, before we continue we should recognize that we have just made a structural change to the model. If we were not careful, this could have introduced new structural issues to the model, so we should start from the beginning just to be sure.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "In general, you should always start from the beginning of the model diagnosis workflow after you make any change to the model. Remember to also record these changes in your log book in case something unexpected happens so that you can revert any changes that cause problems.\n",
+ "
\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Check to see if there are any new structural issues in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 34,
+ "metadata": {
+ "scrolled": true,
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We can see that ``v7`` is potentially causing problems due to having a very small value (on the order of magnitude of the solver tolerance). This can be especially problematic for interior point solvers like IPOPT if there is a lower bound of 0 (which there is in this case). IPOPT tries to avoid bounds and thus perturbs solutions away from these if it gets too close, which can cause convergence to be slow (or fail) if the solution lies close to the bound.\n",
- "\n",
- "We can address this by scaling the variable so that the value of the scaled variable is large enough that the solution is not close to the lower bound. Additionally, we should look at any constraint that ``v7`` appears in (in this case ``c4``) and ensure that those constraints are well scaled as well (so that a residual of 1e-6 is reasonable for the terms involved).\n",
- "\n",
- "For this case, we can set a scaling factor of 1e8 for both ``v7`` and ``c4`` as shown below. Note that we also need to apply Pyomo's scaling transformation to create a new scaled model to work with."
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Activated Blocks: 1 (Deactivated: 0)\n",
+ " Free Variables in Activated Constraints: 4 (External: 0)\n",
+ " Free Variables with only lower bounds: 0\n",
+ " Free Variables with only upper bounds: 0\n",
+ " Free Variables with upper and lower bounds: 2\n",
+ " Fixed Variables in Activated Constraints: 3 (External: 0)\n",
+ " Activated Equality Constraints: 4 (Deactivated: 0)\n",
+ " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
+ " Activated Objectives: 0 (Deactivated: 0)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "0 WARNINGS\n",
+ "\n",
+ " No warnings found!\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 Cautions\n",
+ "\n",
+ " Caution: 1 variable fixed to 0\n",
+ " Caution: 1 unused variable (0 fixed)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " Try to initialize/solve your model and then call report_numerical_issues()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Our change has not introduced any new structural issues, so we can move on and try to solve the model again.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Re-solve the model in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 36,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "code",
- "execution_count": 41,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.scaling_factor = pyo.Suffix(direction=pyo.Suffix.EXPORT)\n",
- "\n",
- "m.scaling_factor[m.v7] = 1e8\n",
- "m.scaling_factor[m.c4] = 1e8\n",
- "\n",
- "scaling = pyo.TransformationFactory('core.scale_model')\n",
- "scaled_model = scaling.create_using(m, rename=False)"
- ]
+ "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...: 7\n",
+ "Number of nonzeros in inequality constraint Jacobian.: 0\n",
+ "Number of nonzeros in Lagrangian Hessian.............: 0\n",
+ "\n",
+ "Total number of variables............................: 4\n",
+ " variables with only lower bounds: 0\n",
+ " variables with lower and upper bounds: 2\n",
+ " variables with only upper bounds: 0\n",
+ "Total number of equality constraints.................: 4\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 6.67e-01 0.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
+ " 1 0.0000000e+00 6.66e-03 2.97e+00 -1.0 2.00e+00 - 7.17e-01 9.90e-01h 1\n",
+ " 2 0.0000000e+00 6.27e-05 9.38e+00 -1.0 2.00e-02 - 1.00e+00 9.91e-01h 1\n",
+ " 3 0.0000000e+00 8.88e-16 1.13e-12 -1.0 1.88e-04 - 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....: 8.8817841970012523e-16 8.8817841970012523e-16\n",
+ "Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Overall NLP error.......: 8.8817841970012523e-16 8.8817841970012523e-16\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"
+ ]
},
{
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Now that we have a scaled model, we can try to solve it and hopefully see better convergence than the unscaled model.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Solve the scaled model and check to see how many iterations are required.\n",
- "
"
+ "data": {
+ "text/plain": [
+ "{'Problem': [{'Lower bound': -inf, 'Upper bound': inf, 'Number of objectives': 1, 'Number of constraints': 4, 'Number of variables': 4, 'Sense': 'unknown'}], 'Solver': [{'Status': 'ok', 'Message': 'Ipopt 3.13.2\\\\x3a Optimal Solution Found', 'Termination condition': 'optimal', 'Id': 0, 'Error rc': 0, 'Time': 0.02317023277282715}], 'Solution': [OrderedDict([('number of solutions', 0), ('number of solutions displayed', 0)])]}"
]
- },
+ },
+ "execution_count": 36,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "solver.solve(m, tee=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "IPOPT should have returned optimal solution now, so it looks like those bounds were what was causing the model to be infeasible. At this point, the model is now solving (for the current values at least), so you might think that the model is now ready for optimization.\n",
+ "\n",
+ "However, if we look at the solver logs we can see that it took around 3 iterations for IPOPT to solve our model (depending on minor variations in computer architecture). For a model this simple, we would generally expect it to solve in only 1 iteration so there is still some room for improvement.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "You should keep in mind that just because you get an optimal solution does not mean that your model is robust and free of issues.\n",
+ " \n",
+ "You should always take the time to look over the solver logs to look for signs of trouble, even if you get an optimal solution. While you might get an optimal solution for the current state, there may be advance warning signs of issues that will cause problems later when you try to solve the model at a different state.\n",
+ "
\n",
+ "\n",
+ "Let us run the ``report_numerical_issues`` method again to see if there are any other problems we need to address.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Check for additional numerical issues in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 38,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "code",
- "execution_count": 43,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "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...: 7\n",
- "Number of nonzeros in inequality constraint Jacobian.: 0\n",
- "Number of nonzeros in Lagrangian Hessian.............: 0\n",
- "\n",
- "Total number of variables............................: 4\n",
- " variables with only lower bounds: 0\n",
- " variables with lower and upper bounds: 2\n",
- " variables with only upper bounds: 0\n",
- "Total number of equality constraints.................: 4\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 5.33e-15 0.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
- "\n",
- "Number of Iterations....: 0\n",
- "\n",
- " (scaled) (unscaled)\n",
- "Objective...............: 0.0000000000000000e+00 0.0000000000000000e+00\n",
- "Dual infeasibility......: 0.0000000000000000e+00 0.0000000000000000e+00\n",
- "Constraint violation....: 5.3290705182007514e-15 5.3290705182007514e-15\n",
- "Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
- "Overall NLP error.......: 5.3290705182007514e-15 5.3290705182007514e-15\n",
- "\n",
- "\n",
- "Number of objective function evaluations = 1\n",
- "Number of objective gradient evaluations = 1\n",
- "Number of equality constraint evaluations = 1\n",
- "Number of inequality constraint evaluations = 0\n",
- "Number of equality constraint Jacobian evaluations = 1\n",
- "Number of inequality constraint Jacobian evaluations = 0\n",
- "Number of Lagrangian Hessian evaluations = 0\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"
- ]
- },
- {
- "data": {
- "text/plain": [
- "{'Problem': [{'Lower bound': -inf, 'Upper bound': inf, 'Number of objectives': 1, 'Number of constraints': 4, 'Number of variables': 4, 'Sense': 'unknown'}], 'Solver': [{'Status': 'ok', 'Message': 'Ipopt 3.13.2\\\\x3a Optimal Solution Found', 'Termination condition': 'optimal', 'Id': 0, 'Error rc': 0, 'Time': 0.0058002471923828125}], 'Solution': [OrderedDict([('number of solutions', 0), ('number of solutions displayed', 0)])]}"
- ]
- },
- "execution_count": 43,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "solver.solve(scaled_model, tee=True)"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Jacobian Condition Number: 1.700E+01\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "0 WARNINGS\n",
+ "\n",
+ " No warnings found!\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "5 Cautions\n",
+ "\n",
+ " Caution: 1 Variable with value close to their bounds (abs=1.0E-04, rel=1.0E-04)\n",
+ " Caution: 1 Variable with value close to zero (tol=1.0E-08)\n",
+ " Caution: 1 Variable with extreme value (<1.0E-04 or >1.0E+04)\n",
+ " Caution: 1 Variable with None value\n",
+ " Caution: 1 extreme Jacobian Entry (<1.0E-04 or >1.0E+04)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " If you still have issues converging your model consider:\n",
+ " prepare_svd_toolbox()\n",
+ " prepare_degeneracy_hunter()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.report_numerical_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The toolbox is not reporting any warnings which is good, however there are still 5 numerical cautions that it has identified which might be contributing to the larger than expected number of iterations. As mentioned earlier, the toolbox does not suggest methods for investigating these, but there are methods available. For example, we can look at the variable with an extreme value using the `display_variables_with_extreme_values()` method.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Check for additional information about variables with extreme values.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 40,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "As we can see, the scaled model solved in 0 iterations (indicating that it already had the right solution). However, had we done this to the unscaled model we would have found it required 2-3 iterations again due to IPOPT perturbing the initial (correct) solution away from the bounds.\n",
- "\n",
- "\n",
- "Warning:\n",
- "Normally in these cases we would need to map the solution from the scaled model back to the unscaled model so we can view the results. In this case, we are not actually interested in the solution so we move on with the model diagnosis.\n",
- "
\n",
- "\n",
- "Now that we have fixed the scaling issues, we can go back to the ``DiagnosticsToolbox`` and see if we still have any warnings. Note however that we need to look at the scaled model now rather than the original model, so we need to create a new instance of the ``DiagnositcsToolbox`` with the scaled model as the ``model`` argument.\n",
- "\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Create a new instance of the DiagnosticsToolbox and check the scaled model for issues.\n",
- "
"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "The following variable(s) have extreme values (<1.0E-04 or > 1.0E+04):\n",
+ "\n",
+ " v7: 4.9999999999999945e-08\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.display_variables_with_extreme_values()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We can see that ``v7`` is potentially causing problems due to having a very small value (on the order of magnitude of the solver tolerance). This can be especially problematic for interior point solvers like IPOPT if there is a lower bound of 0 (which there is in this case). IPOPT tries to avoid bounds and thus perturbs solutions away from these if it gets too close, which can cause convergence to be slow (or fail) if the solution lies close to the bound.\n",
+ "\n",
+ "We can address this by scaling the variable so that the value of the scaled variable is large enough that the solution is not close to the lower bound. Additionally, we should look at any constraint that ``v7`` appears in (in this case ``c4``) and ensure that those constraints are well scaled as well (so that a residual of 1e-6 is reasonable for the terms involved).\n",
+ "\n",
+ "For this case, we can set a scaling factor of 1e8 for both ``v7`` and ``c4`` as shown below. Note that we also need to apply Pyomo's scaling transformation to create a new scaled model to work with."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 41,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.scaling_factor = pyo.Suffix(direction=pyo.Suffix.EXPORT)\n",
+ "\n",
+ "m.scaling_factor[m.v7] = 1e8\n",
+ "m.scaling_factor[m.c4] = 1e8\n",
+ "\n",
+ "scaling = pyo.TransformationFactory(\"core.scale_model\")\n",
+ "scaled_model = scaling.create_using(m, rename=False)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now that we have a scaled model, we can try to solve it and hopefully see better convergence than the unscaled model.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Solve the scaled model and check to see how many iterations are required.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 43,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "code",
- "execution_count": 46,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Model Statistics\n",
- "\n",
- " Jacobian Condition Number: 1.800E+01\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "0 WARNINGS\n",
- "\n",
- " No warnings found!\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "3 Cautions\n",
- "\n",
- " Caution: 1 Variable with value close to their bounds (abs=1.0E-04, rel=1.0E-04)\n",
- " Caution: 1 Variable with value close to zero (tol=1.0E-08)\n",
- " Caution: 1 Variable with None value\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "Suggested next steps:\n",
- "\n",
- " If you still have issues converging your model consider:\n",
- " prepare_svd_toolbox()\n",
- " prepare_degeneracy_hunter()\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt_scaled = DiagnosticsToolbox(scaled_model)\n",
- "dt_scaled.report_numerical_issues()"
- ]
+ "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...: 7\n",
+ "Number of nonzeros in inequality constraint Jacobian.: 0\n",
+ "Number of nonzeros in Lagrangian Hessian.............: 0\n",
+ "\n",
+ "Total number of variables............................: 4\n",
+ " variables with only lower bounds: 0\n",
+ " variables with lower and upper bounds: 2\n",
+ " variables with only upper bounds: 0\n",
+ "Total number of equality constraints.................: 4\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 5.33e-15 0.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
+ "\n",
+ "Number of Iterations....: 0\n",
+ "\n",
+ " (scaled) (unscaled)\n",
+ "Objective...............: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Dual infeasibility......: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Constraint violation....: 5.3290705182007514e-15 5.3290705182007514e-15\n",
+ "Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Overall NLP error.......: 5.3290705182007514e-15 5.3290705182007514e-15\n",
+ "\n",
+ "\n",
+ "Number of objective function evaluations = 1\n",
+ "Number of objective gradient evaluations = 1\n",
+ "Number of equality constraint evaluations = 1\n",
+ "Number of inequality constraint evaluations = 0\n",
+ "Number of equality constraint Jacobian evaluations = 1\n",
+ "Number of inequality constraint Jacobian evaluations = 0\n",
+ "Number of Lagrangian Hessian evaluations = 0\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"
+ ]
},
{
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We can see that applying scaling addressed two of the cautions we had before (the variable with an extreme value and an associated large value in the model Jacobian). Whilst we were able to solve the unscaled model in this case, this is in part because it was a simple linear model. In more complex, non-linear models, scaling becomes much more important and often depends strongly on the current state of the model. That is, you can often find cases where the unscaled (or poorly scaled) model solves for a limited range of conditions but fails to solve if you move too far away for the current state. Whilst you might be able to solve the model at the current state, you should always check the solver logs and numerical cautions for advanced warning signs of scaling issues that might manifest later when you try to solve the model for a different state (e.g., during optimization).\n",
- "\n",
- "\n",
- "Warning:\n",
- "By their nature, numerical issues depend on the current values of the variables in the model, and thus may remain hidden until someone tries to solve the model close to where the issue exists. For this reason, the full model diagnostics workflow contains steps to run the numerical checks across a wide range of variable values to try to ensure that no issues remain hidden. This is beyond the scope of this tutorial however.\n",
- "
\n",
- "\n",
- "At this point, we have addressed all the issues that were preventing us from solving the demonstration model and so reached the end of this tutorial. For cases where we are still having trouble solving the model, we can see that the toolbox is suggesting additional methods for further debugging and these advanced features will be the focus of separate tutorials."
+ "data": {
+ "text/plain": [
+ "{'Problem': [{'Lower bound': -inf, 'Upper bound': inf, 'Number of objectives': 1, 'Number of constraints': 4, 'Number of variables': 4, 'Sense': 'unknown'}], 'Solver': [{'Status': 'ok', 'Message': 'Ipopt 3.13.2\\\\x3a Optimal Solution Found', 'Termination condition': 'optimal', 'Id': 0, 'Error rc': 0, 'Time': 0.0058002471923828125}], 'Solution': [OrderedDict([('number of solutions', 0), ('number of solutions displayed', 0)])]}"
]
+ },
+ "execution_count": 43,
+ "metadata": {},
+ "output_type": "execute_result"
}
- ],
- "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"
+ ],
+ "source": [
+ "solver.solve(scaled_model, tee=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As we can see, the scaled model solved in 0 iterations (indicating that it already had the right solution). However, had we done this to the unscaled model we would have found it required 2-3 iterations again due to IPOPT perturbing the initial (correct) solution away from the bounds.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "Normally in these cases we would need to map the solution from the scaled model back to the unscaled model so we can view the results. In this case, we are not actually interested in the solution so we move on with the model diagnosis.\n",
+ "
\n",
+ "\n",
+ "Now that we have fixed the scaling issues, we can go back to the ``DiagnosticsToolbox`` and see if we still have any warnings. Note however that we need to look at the scaled model now rather than the original model, so we need to create a new instance of the ``DiagnosticsToolbox`` with the scaled model as the ``model`` argument.\n",
+ "\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Create a new instance of the DiagnosticsToolbox and check the scaled model for issues.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 46,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Jacobian Condition Number: 1.800E+01\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "0 WARNINGS\n",
+ "\n",
+ " No warnings found!\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "3 Cautions\n",
+ "\n",
+ " Caution: 1 Variable with value close to their bounds (abs=1.0E-04, rel=1.0E-04)\n",
+ " Caution: 1 Variable with value close to zero (tol=1.0E-08)\n",
+ " Caution: 1 Variable with None value\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " If you still have issues converging your model consider:\n",
+ " prepare_svd_toolbox()\n",
+ " prepare_degeneracy_hunter()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
}
+ ],
+ "source": [
+ "dt_scaled = DiagnosticsToolbox(scaled_model)\n",
+ "dt_scaled.report_numerical_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We can see that applying scaling addressed two of the cautions we had before (the variable with an extreme value and an associated large value in the model Jacobian). Whilst we were able to solve the unscaled model in this case, this is in part because it was a simple linear model. In more complex, non-linear models, scaling becomes much more important and often depends strongly on the current state of the model. That is, you can often find cases where the unscaled (or poorly scaled) model solves for a limited range of conditions but fails to solve if you move too far away for the current state. Whilst you might be able to solve the model at the current state, you should always check the solver logs and numerical cautions for advanced warning signs of scaling issues that might manifest later when you try to solve the model for a different state (e.g., during optimization).\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "By their nature, numerical issues depend on the current values of the variables in the model, and thus may remain hidden until someone tries to solve the model close to where the issue exists. For this reason, the full model diagnostics workflow contains steps to run the numerical checks across a wide range of variable values to try to ensure that no issues remain hidden. This is beyond the scope of this tutorial however.\n",
+ "
\n",
+ "\n",
+ "At this point, we have addressed all the issues that were preventing us from solving the demonstration model and so reached the end of this tutorial. For cases where we are still having trouble solving the model, we can see that the toolbox is suggesting additional methods for further debugging and these advanced features will be the focus of separate tutorials."
+ ]
+ }
+ ],
+ "metadata": {
+ "celltoolbar": "Tags",
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
},
- "nbformat": 4,
- "nbformat_minor": 3
+ "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
}
\ No newline at end of file
diff --git a/idaes_examples/notebooks/docs/diagnostics/diagnostics_toolbox_exercise.ipynb b/idaes_examples/notebooks/docs/diagnostics/diagnostics_toolbox_exercise.ipynb
index dd680878..aa02ff4f 100644
--- a/idaes_examples/notebooks/docs/diagnostics/diagnostics_toolbox_exercise.ipynb
+++ b/idaes_examples/notebooks/docs/diagnostics/diagnostics_toolbox_exercise.ipynb
@@ -1,786 +1,788 @@
{
- "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": [
- "# IDAES Model Diagnostics Toolbox Tutorial\n",
- "Author: Andrew Lee \n",
- "Maintainer: Andrew Lee \n",
- "Updated: 2023-10-31 \n",
- "\n",
- "As you have likely discovered already, developing and solving models in an equation-oriented (EO) environment can be challenging and often takes a significant amount of effort. There are many pitfalls and mistakes that can be encountered when developing a model which can greatly impact the solvability and robustness of the final problem.\n",
- "\n",
- "Model diagnosis and debugging is often more of an art than a science, and it generally relies on significant experience and understanding both of general EO modeling techniques and the specific model and problem being solved. To assist with this process, IDAES has developed a model diagnostics toolbox that brings together a large number of tools for identifying potential issues in a model to help guide the user through the process of finding and resolving these issues. Note however that whilst these tools can help identify the presence of an issue, remedying the issue always requires some degree of engineering knowledge about the system being modeled, and thus it is ultimately up to the user to find a solution to the problem.\n",
- "\n",
- "This tutorial will take you through using the ``DiagnosticsToolbox`` to debug a number of issues in a simple Pyomo model and to take it from initially reporting a possible infeasible solution to returning the correct solution.\n",
- "\n",
- "To get started, the ``DiagnosticsToolbox`` can be imported from ``idaes.core.util``.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Import the DiagnosticsToolbox in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "from idaes.core.util import DiagnosticsToolbox"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "To get some information on where to start, try using the Python ``help()`` function to see the documentation for the ``DiagnosticsToolbox``.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Call `help(DiagnosticsToolbox)` to see some more information on the toolbox and some instructions on how to get started.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Call the help() function for more information"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The ``help()`` function gives us a lot of information on the ``DiagnosticsToolbox`` and all the methods that it supports (and there are many). However, the important part to start with are the four steps outlined at the top of the doc string that tell us how to get started.\n",
- "\n",
- "Firstly, we need a model to test (and, for this tutorial at least, one that has a wide range of issues that we need to fix before it will solve). We then also need to fix some variables so that we have 0 degrees of freedom in our model. Whilst our ultimate goal is generally optimization (and thus a system with 1 or more degrees of freedom), all models conceptually derive from a square model representing a nominal state. If this nominal state is not well-posed, then any issues present will also be present in the resulting optimization (even if adding degrees of freedom means that the model is now easier to solve).\n",
- "\n",
- "The cell below contains a demonstration model for this tutorial that contains a number of issues that we will resolve using the ``DiagnosticsToolbox``."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [],
- "source": [
- "import pyomo.environ as pyo\n",
- "\n",
- "m = pyo.ConcreteModel()\n",
- "\n",
- "m.v1 = pyo.Var(units=pyo.units.m)\n",
- "m.v2 = pyo.Var(units=pyo.units.m)\n",
- "m.v3 = pyo.Var(bounds=(0, 5))\n",
- "m.v4 = pyo.Var()\n",
- "m.v5 = pyo.Var(bounds=(0, 10))\n",
- "m.v6 = pyo.Var()\n",
- "m.v7 = pyo.Var(units=pyo.units.m, bounds=(0, 1)) # Poorly scaled variable with lower bound\n",
- "m.v8 = pyo.Var() # unused variable\n",
- "\n",
- "m.c1 = pyo.Constraint(expr=m.v1 + m.v2 == 10) # Unit consistency issue\n",
- "m.c2 = pyo.Constraint(expr=m.v3 == m.v4 + m.v5)\n",
- "m.c3 = pyo.Constraint(expr=2*m.v3 == 3*m.v4 + 4*m.v5 + m.v6)\n",
- "m.c4 = pyo.Constraint(expr=m.v7 == 1e-8*m.v1) # Poorly scaled constraint\n",
- "\n",
- "m.v4.fix(2)\n",
- "m.v5.fix(2)\n",
- "m.v6.fix(0)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Next, the instructions tell us to create an instance of the ``DiagnosticsToolbox`` and to pass the model we wish to examine as an argument.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Create an instance of the DiagnosticsToolbox: dt = DiagnosticsToolbox(m)\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Create an instance of the Diagnostics Toolbox"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Finally, the instructions tell us to run the ``report_structural_issues()`` method. Structural issues represent issues that exist solely in the form of the model equations and thus do not depend on the current value of any of the variables. This is useful as it means we can check for these before we even call a solver, which can be critical as sometimes these issues will cause a solver to fail without providing a useful solution.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Call dt.report_structural_issues() in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Call the report_strucutral_issues() method"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Looking at the output from the ``report_structural_issues()`` method, we can see that it provides a fairly short summary containing 4 sections.\n",
- "\n",
- "1. The first section is a summary of the size of the model, indicating things like the number of variables and constraints. The size of the model is often important for judging how difficult it will be to solve, and this information can also be useful for comparison to what is being sent to the solver. Most solvers will report the size of the model in their output logs, and if there is a difference between what is reported here and by the solver, then you should probably look into what is happening. This section also notes some things such as if you have any deactivated Blocks, Constraints or Objectives, or if you have variables which appear in the constraints that are not part of the model; these are not necessarily wrong but it is easy to have accidentally deactivated something you did not intend to so you should always check to see that these are expected.\n",
- "\n",
- "2. The second section provides a summary of any critical structural issues that were found - in this case we can see that there are 2 warnings we are going to need to look into. Warnings represent issues that need to be addressed before moving on as these will likely cause the solver to fail or give an incorrect answer.\n",
- "\n",
- "3. The third section lists a summary of any cautions that are found. Cautions represent issues that may or may not be problematic; in many cases these might be expected behaviors or borderline issues. However, these could also represent conceptual issues that should be addressed, so users should take the time to investigate these and determine if they need to be fixed or not.\n",
- "\n",
- "4. Finally, there is a section that suggests the next steps to take to help guide you through the model diagnosis process. If any warnings were identified, this section will list methods that can help you get more information on each specific problem, and if no warnings are found then it will guide you onto the next step in the model diagnosis workflow.\n",
- "\n",
- "**Note:** there are methods available to help investigate cautions as well, but these will not show up in the next steps in order to avoid cluttering the output. You can get more information on the available methods for investigating cautions via the documentation or ``help()`` function.\n",
- "\n",
- "In our current model, we have 2 critical issues (warnings) that we need to look into and resolve. The order in which we resolve these will generally not matter, but be aware that these can often be interrelated - fixing one warning might resolve other warnings as well (or create new ones), and sometimes you will need to look at multiple issues together to find the overall root cause.\n",
- "\n",
- "To start with, let us look at the unit consistency issue. From the \"Next Steps\" section above, the toolbox is suggesting we run the ``display_components_with_inconsistent_units()`` method for more information.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Call the `display_components_with_inconsistent_units()` method from the DiagnosticsToolbox to see more information on which constraint is causing the unit consistency issues.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Call the display_components_with_inconsistent_units() method"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "This tells us that the issue lies in constraint ``c1``. If we go back and look at this constraint, we can see that it says ``v1 + v2 == 10``. ``v1`` and ``v2`` both have units of ``m`` which is consistent, but the constant in the expression (right hand side) is unitless. Thus, we need to correct this so that the right hand side has units for the constraint to be consistent.\n",
- "\n",
- "The cell below shows how to delete a constraint and replace it with a new one with the correct units.\n",
- "\n",
- "\n",
- "Warning:\n",
- "Deleting components can cause unexpected issues if something else in a model is using that component (e.g., deleting a variable which is used in a constraint). You should always be careful when deleting Pyomo components and make sure you only delete components that are not used elsewhere.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 12,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Delete the incorrect Constraint\n",
- "m.del_component(m.c1)\n",
- "\n",
- "# Re-create the Constraint with the correct units\n",
- "m.c1 = pyo.Constraint(expr=m.v1 + m.v2 == 10*pyo.units.m)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Warning:\n",
- "Fixing issues in models is often an iterative process requiring trial and error. You might also have some results from a model before running the diagnostics tools and the changes you make during debugging may make it difficult to replicate those results afterwards.\n",
- " \n",
- "It is strongly recommended that you keep a record of the changes you make at each step and why, along with a Git hash (or similar version control marker) corresponding to these changes. This will allow you see what changes and why, and give you a way to go back to previous iterations if the current approach does not work out. The IDAES documentation contains recommendations on how to keep and maintain a modeling logbook.\n",
- "
\n",
- "\n",
- "Now, re-run the ``report_structural_issues()`` method and see if this change has fixed the unit consistency issue.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Call dt.report_structural_issues() in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 13,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Call the report_structural_issues() method"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The unit consistency issue has been resolved by the changes above, so now we need to look at the structural singularity. A structural singularity occurs when one sub-part of the model is over-constrained (negative degrees of freedom), which generally means another part is under-constrained (positive degrees of freedom, assuming that there are 0 degrees of freedom overall).\n",
- "\n",
- "The toolbox is suggesting we use the ``display_overconstrained_set()`` and ``display_underconstrained_set()`` methods to get more information on the singularity; for now, let us start with the over-constrained set.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Call dt.display_overconstrained_set() in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 15,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Call the display_overconstrained_set() method"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "From the output above, the toolbox is telling us that we have two constraints (``c2`` and ``c3``) which only contain a single unfixed variable (``v3``); thus in this part of the model we have -1 degree of freedom and the model is not well defined (structurally singular). If we go back and look at these constraints, we can see the that the constraints are:\n",
- "\n",
- "``c2: v3 == v4 + v5``\n",
- "\n",
- "``c3: 2*v3 == 3*v4 + 4*v5 + v6``\n",
- "\n",
- "We can see that in addition to ``v3`` these constraints actually contain 3 other variables (``v4``, ``v5`` and ``v6``), however these are all variables we fixed to get our initial zero degrees of freedom. It looks like we have either accidentally fixed one too many variables or written one too many constraints.\n",
- "\n",
- "For this example, let us assume that ``v4`` was not supposed to be fixed and unfix it.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Resolve the structural singularity and then call dt.report_structural_issues() in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 17,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Unfix v4\n",
- "\n",
- "# Then call the report_structural_issues() method again"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We can see that the over-constrained set is now empty (0 variables and 0 constraints) but the under-constrained set still has 3 variables and only 2 constraints. We can also see that there is a new warning about having 1 degree of freedom in the model, however this should not be surprising as we have just unfixed ``v4`` to resolve the over-constrained set so we have added a degree of freedom to the model.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Display the under-constrained set in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 19,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Display the under-constrained set"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Looking at the output from the ``display_underconstrained_set()`` method, we can see that we have two constraints, ``c1`` and ``c4``, which contain three unfixed variables, ``v1``, ``v2`` and ``v7``. Thus, we have one degree of freedom that needs to be addressed. To fix this, we could either fix one of the variables shown or add an additional equality constraint to the model.\n",
- "\n",
- "For this example let's fix ``v2`` to a value of 5 and then re-run the ``report_structural_issues()`` method.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Fix v2 to a value of 5 and then re-run dt.report_structural_issues.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 21,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Fix v2 = 5\n",
- "\n",
- "# Then re-run report_structural_issues() method"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The toolbox is now telling us that no warnings were found, so we have resolved all the structural issues (for now at least). The toolbox is telling us that there are also 2 non-critical issues (cautions) that we should look at; one about an unused variable and one about a variable fixed to zero. If you wish, you can look into identifying and fixing these yourself, however for this example we will move on to the next step (remember that the toolbox has methods to display more details for each of these which you can find in the documentation or from the ``help()`` function).\n",
- "\n",
- "For the Next Steps section, the toolbox is recommending we try to solve our model and then check for numerical issues.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Use the Pyomo SolverFactory to create an instance of IPOPT and then try to solve the model. Make sure to set \"tee=True\" as this is going to fail (and it is always good practice to review the solver logs).\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 23,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Create a solver object\n",
- "\n",
- "# Try to solve the model"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "As hinted at above, IPOPT has returned a warning that the problem may be infeasible. Before moving on however, it is always good practice to look over the solver outputs and see what it is telling you.\n",
- "\n",
- "\n",
- "Warning:\n",
- "A lot of useful information is contained in the solver logs which is extremely useful when diagnosing modeling issues. Each solver has its own way of reporting output and its own specific behavior, so you will need to learn to interpret the output of each solver you use. The IDAES Documentation contains some guidance on interpreting output logs for a few common solvers.\n",
- "
\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Call the report_numerical_issues method in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 25,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Check for numerical issues"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The ``report_numerical_issues()`` provides a summary similar to that which we saw for the structural issues. Firstly, it reports to us the Jacobian condition number for our problem which can give us an idea of how well-scaled the problem is, followed by a list of warnings, cautions and suggested next steps.\n",
- "\n",
- "Unsurprisingly, we are seeing a warning about a constraint with a large residual which we would expect when a solver reports a potentially infeasible problem. We are also seeing a warning about a variable with bound violations which might be contributing to the potential infeasibility.\n",
- "\n",
- "For the next steps, the toolbox is suggesting some new methods to get more information on these issues; let us start by looking at the constraints with large residuals.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Display the constraint with a large residual in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 27,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Display constraint with large residual"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The toolbox is telling us that the constraint which failed to converge is ``c2``, however this is generally only part of the story. Solvers work by trying to minimize the infeasibility in the model (residual of the constraints), which generally means they push any infeasibility onto the least sensitive constraint in the problem. Thus, the constraint which shows the infeasibility is often not the root cause of the problem, but only the symptom of the underlying issue.\n",
- "\n",
- "If we look back at the constraints, we can see that the same variables also appear in ``c3`` and that some of these have bounds, all of which could be contributing to the infeasibility. In this case the solver tried to minimize the residual in all the constraints and ended up pushing all the issues off onto ``c2``.\n",
- "\n",
- "\n",
- "Warning:\n",
- "When dealing with solver issues such as this, you should always remember that the obvious symptoms are often just the tip of the iceberg and that the real issue generally lies somewhere else; the challenge is tracing the symptoms back to their ultimate source.\n",
- "
\n",
- "\n",
- "Next, let us take a look at the variables at or outside their bounds as well. When a solver reports an potentially infeasible solution, the most common cause is unexpected bounds violations so you should always check these first.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Display the variables with bounds violations.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 29,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Display the variables with bounds violations"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The toolbox is telling us that ``v3`` is the variable with a potential issue. It is also showing us the current value and bounds for ``v3`` as well as if it is a fixed or free variable, which will be useful for diagnosing the issues.\n",
- "\n",
- "We can see that ``v3`` is a free variable with bounds between 0 and 5 and a current value of 0. As ``v3`` is a free variable, this suggests that the solver has pushed the value to the bound where it cannot go any further, and this might be part of the cause of our infeasibility.\n",
- "\n",
- "\n",
- "Warning:\n",
- "When dealing with bounds violations you should always start by understanding why the bounds exist and what they mean - in many cases a bound indicates the range over which the model can be trusted and that going beyond this may result in unexpected behavior due to extrapolation.\n",
- " \n",
- "Never arbitrarily change a bound just because it is causing your model to be infeasible without understanding the consequences of this decision. Often, a bound violation is an indication that you need to re-think some of the constraints in your model to find alternatives which are valid in the actual range of values you are trying to solve for.\n",
- "
\n",
- "\n",
- "For this example, let us assume that we made a mistake with the bounds on ``v3`` and set the lower bound to be -5.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Update the bounds on v3 in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 31,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Update bounds for v3"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Now that we have fixed the bounds issues, we should check whether our model is now feasible. However, before we continue we should recognize that we have just made a structural change to the model. If we were not careful, this could have introduced new structural issues to the model, so we should start from the beginning just to be sure.\n",
- "\n",
- "\n",
- "Warning:\n",
- "In general, you should always start from the beginning of the model diagnosis workflow after you make any change to the model. Remember to also record these changes in your log book in case something unexpected happens so that you can revert any changes that cause problems.\n",
- "
\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Check to see if there are any new structural issues in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 33,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Check for new strucutral issues"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Our change has not introduced any new structural issues, so we can move on and try to solve the model again.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Re-solve the model in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 35,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Re-solve the model"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "IPOPT should have returned optimal solution now, so it looks like those bounds were what was causing the model to be infeasible. At this point, the model is now solving (for the current values at least), so you might think that the model is now ready for optimization.\n",
- "\n",
- "However, if we look at the solver logs we can see that it took around 3 iterations for IPOPT to solve our model (depending on minor variations in computer architecture). For a model this simple, we would generally expect it to solve in only 1 iteration so there is still some room for improvement.\n",
- "\n",
- "\n",
- "Warning:\n",
- "You should keep in mind that just because you get an optimal solution does not mean that your model is robust and free of issues.\n",
- " \n",
- "You should always take the time to look over the solver logs to look for signs of trouble, even if you get an optimal solution. While you might get an optimal solution for the current state, there may be advance warning signs of issues that will cause problems later when you try to solve the model at a different state.\n",
- "
\n",
- "\n",
- "Let us run the ``report_numerical_issues`` method again to see if there are any other problems we need to address.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Check for additional numerical issues in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 37,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Check for additional numerical issues"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The toolbox is not reporting any warnings which is good, however there are still 5 numerical cautions that it has identified which might be contributing to the larger than expected number of iterations. As mentioned earlier, the toolbox does not suggest methods for investigating these, but there are methods available. For example, we can look at the variable with an extreme value using the `display_variables_with_extreme_values()` method.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Check for additional information about variables with extreme values.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 39,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Display variable with extreme value"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We can see that ``v7`` is potentially causing problems due to having a very small value (on the order of magnitude of the solver tolerance). This can be especially problematic for interior point solvers like IPOPT if there is a lower bound of 0 (which there is in this case). IPOPT tries to avoid bounds and thus perturbs solutions away from these if it gets too close, which can cause convergence to be slow (or fail) if the solution lies close to the bound.\n",
- "\n",
- "We can address this by scaling the variable so that the value of the scaled variable is large enough that the solution is not close to the lower bound. Additionally, we should look at any constraint that ``v7`` appears in (in this case ``c4``) and ensure that those constraints are well scaled as well (so that a residual of 1e-6 is reasonable for the terms involved).\n",
- "\n",
- "For this case, we can set a scaling factor of 1e8 for both ``v7`` and ``c4`` as shown below. Note that we also need to apply Pyomo's scaling transformation to create a new scaled model to work with."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 41,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.scaling_factor = pyo.Suffix(direction=pyo.Suffix.EXPORT)\n",
- "\n",
- "m.scaling_factor[m.v7] = 1e8\n",
- "m.scaling_factor[m.c4] = 1e8\n",
- "\n",
- "scaling = pyo.TransformationFactory('core.scale_model')\n",
- "scaled_model = scaling.create_using(m, rename=False)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Now that we have a scaled model, we can try to solve it and hopefully see better convergence than the unscaled model.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Solve the scaled model and check to see how many iterations are required.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 42,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Solve scaled model"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "As we can see, the scaled model solved in 0 iterations (indicating that it already had the right solution). However, had we done this to the unscaled model we would have found it required 2-3 iterations again due to IPOPT perturbing the initial (correct) solution away from the bounds.\n",
- "\n",
- "\n",
- "Warning:\n",
- "Normally in these cases we would need to map the solution from the scaled model back to the unscaled model so we can view the results. In this case, we are not actually interested in the solution so we move on with the model diagnosis.\n",
- "
\n",
- "\n",
- "Now that we have fixed the scaling issues, we can go back to the ``DiagnosticsToolbox`` and see if we still have any warnings. Note however that we need to look at the scaled model now rather than the original model, so we need to create a new instance of the ``DiagnositcsToolbox`` with the scaled model as the ``model`` argument.\n",
- "\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Create a new instance of the DiagnosticsToolbox and check the scaled model for issues.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 45,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Create a new diagnostics toolbox for scaled model\n",
- "\n",
- "# Report numerical issues for scaled model\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We can see that applying scaling addressed two of the cautions we had before (the variable with an extreme value and an associated large value in the model Jacobian). Whilst we were able to solve the unscaled model in this case, this is in part because it was a simple linear model. In more complex, non-linear models, scaling becomes much more important and often depends strongly on the current state of the model. That is, you can often find cases where the unscaled (or poorly scaled) model solves for a limited range of conditions but fails to solve if you move too far away for the current state. Whilst you might be able to solve the model at the current state, you should always check the solver logs and numerical cautions for advanced warning signs of scaling issues that might manifest later when you try to solve the model for a different state (e.g., during optimization).\n",
- "\n",
- "\n",
- "Warning:\n",
- "By their nature, numerical issues depend on the current values of the variables in the model, and thus may remain hidden until someone tries to solve the model close to where the issue exists. For this reason, the full model diagnostics workflow contains steps to run the numerical checks across a wide range of variable values to try to ensure that no issues remain hidden. This is beyond the scope of this tutorial however.\n",
- "
\n",
- "\n",
- "At this point, we have addressed all the issues that were preventing us from solving the demonstration model and so reached the end of this tutorial. For cases where we are still having trouble solving the model, we can see that the toolbox is suggesting additional methods for further debugging and these advanced features will be the focus of separate tutorials."
- ]
- }
- ],
- "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
+ "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": [
+ "# IDAES Model Diagnostics Toolbox Tutorial\n",
+ "Author: Andrew Lee \n",
+ "Maintainer: Andrew Lee \n",
+ "Updated: 2023-10-31 \n",
+ "\n",
+ "As you have likely discovered already, developing and solving models in an equation-oriented (EO) environment can be challenging and often takes a significant amount of effort. There are many pitfalls and mistakes that can be encountered when developing a model which can greatly impact the solvability and robustness of the final problem.\n",
+ "\n",
+ "Model diagnosis and debugging is often more of an art than a science, and it generally relies on significant experience and understanding both of general EO modeling techniques and the specific model and problem being solved. To assist with this process, IDAES has developed a model diagnostics toolbox that brings together a large number of tools for identifying potential issues in a model to help guide the user through the process of finding and resolving these issues. Note however that whilst these tools can help identify the presence of an issue, remedying the issue always requires some degree of engineering knowledge about the system being modeled, and thus it is ultimately up to the user to find a solution to the problem.\n",
+ "\n",
+ "This tutorial will take you through using the {py:class}`DiagnosticsToolbox ` to debug a number of issues in a simple Pyomo model and to take it from initially reporting a possible infeasible solution to returning the correct solution.\n",
+ "\n",
+ "To get started, the ``DiagnosticsToolbox`` can be imported from ``idaes.core.util``.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Import the DiagnosticsToolbox in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from idaes.core.util import DiagnosticsToolbox"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "To get some information on where to start, try using the Python ``help()`` function to see the documentation for the ``DiagnosticsToolbox``.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Call `help(DiagnosticsToolbox)` to see some more information on the toolbox and some instructions on how to get started.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Call the help() function for more information"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The ``help()`` function gives us a lot of information on the ``DiagnosticsToolbox`` and all the methods that it supports (and there are many). However, the important part to start with are the four steps outlined at the top of the doc string that tell us how to get started.\n",
+ "\n",
+ "Firstly, we need a model to test (and, for this tutorial at least, one that has a wide range of issues that we need to fix before it will solve). We then also need to fix some variables so that we have 0 degrees of freedom in our model. Whilst our ultimate goal is generally optimization (and thus a system with 1 or more degrees of freedom), all models conceptually derive from a square model representing a nominal state. If this nominal state is not well-posed, then any issues present will also be present in the resulting optimization (even if adding degrees of freedom means that the model is now easier to solve).\n",
+ "\n",
+ "The cell below contains a demonstration model for this tutorial that contains a number of issues that we will resolve using the ``DiagnosticsToolbox``."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import pyomo.environ as pyo\n",
+ "\n",
+ "m = pyo.ConcreteModel()\n",
+ "\n",
+ "m.v1 = pyo.Var(units=pyo.units.m)\n",
+ "m.v2 = pyo.Var(units=pyo.units.m)\n",
+ "m.v3 = pyo.Var(bounds=(0, 5))\n",
+ "m.v4 = pyo.Var()\n",
+ "m.v5 = pyo.Var(bounds=(0, 10))\n",
+ "m.v6 = pyo.Var()\n",
+ "m.v7 = pyo.Var(\n",
+ " units=pyo.units.m, bounds=(0, 1)\n",
+ ") # Poorly scaled variable with lower bound\n",
+ "m.v8 = pyo.Var() # unused variable\n",
+ "\n",
+ "m.c1 = pyo.Constraint(expr=m.v1 + m.v2 == 10) # Unit consistency issue\n",
+ "m.c2 = pyo.Constraint(expr=m.v3 == m.v4 + m.v5)\n",
+ "m.c3 = pyo.Constraint(expr=2 * m.v3 == 3 * m.v4 + 4 * m.v5 + m.v6)\n",
+ "m.c4 = pyo.Constraint(expr=m.v7 == 1e-8 * m.v1) # Poorly scaled constraint\n",
+ "\n",
+ "m.v4.fix(2)\n",
+ "m.v5.fix(2)\n",
+ "m.v6.fix(0)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Next, the instructions tell us to create an instance of the ``DiagnosticsToolbox`` and to pass the model we wish to examine as an argument.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Create an instance of the DiagnosticsToolbox: dt = DiagnosticsToolbox(m)\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Create an instance of the Diagnostics Toolbox"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Finally, the instructions tell us to run the ``report_structural_issues()`` method. Structural issues represent issues that exist solely in the form of the model equations and thus do not depend on the current value of any of the variables. This is useful as it means we can check for these before we even call a solver, which can be critical as sometimes these issues will cause a solver to fail without providing a useful solution.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Call dt.report_structural_issues() in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Call the report_structural_issues() method"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Looking at the output from the ``report_structural_issues()`` method, we can see that it provides a fairly short summary containing 4 sections.\n",
+ "\n",
+ "1. The first section is a summary of the size of the model, indicating things like the number of variables and constraints. The size of the model is often important for judging how difficult it will be to solve, and this information can also be useful for comparison to what is being sent to the solver. Most solvers will report the size of the model in their output logs, and if there is a difference between what is reported here and by the solver, then you should probably look into what is happening. This section also notes some things such as if you have any deactivated Blocks, Constraints or Objectives, or if you have variables which appear in the constraints that are not part of the model; these are not necessarily wrong but it is easy to have accidentally deactivated something you did not intend to so you should always check to see that these are expected.\n",
+ "\n",
+ "2. The second section provides a summary of any critical structural issues that were found - in this case we can see that there are 2 warnings we are going to need to look into. Warnings represent issues that need to be addressed before moving on as these will likely cause the solver to fail or give an incorrect answer.\n",
+ "\n",
+ "3. The third section lists a summary of any cautions that are found. Cautions represent issues that may or may not be problematic; in many cases these might be expected behaviors or borderline issues. However, these could also represent conceptual issues that should be addressed, so users should take the time to investigate these and determine if they need to be fixed or not.\n",
+ "\n",
+ "4. Finally, there is a section that suggests the next steps to take to help guide you through the model diagnosis process. If any warnings were identified, this section will list methods that can help you get more information on each specific problem, and if no warnings are found then it will guide you onto the next step in the model diagnosis workflow.\n",
+ "\n",
+ "**Note:** there are methods available to help investigate cautions as well, but these will not show up in the next steps in order to avoid cluttering the output. You can get more information on the available methods for investigating cautions via the documentation or ``help()`` function.\n",
+ "\n",
+ "In our current model, we have 2 critical issues (warnings) that we need to look into and resolve. The order in which we resolve these will generally not matter, but be aware that these can often be interrelated - fixing one warning might resolve other warnings as well (or create new ones), and sometimes you will need to look at multiple issues together to find the overall root cause.\n",
+ "\n",
+ "To start with, let us look at the unit consistency issue. From the \"Next Steps\" section above, the toolbox is suggesting we run the ``display_components_with_inconsistent_units()`` method for more information.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Call the `display_components_with_inconsistent_units()` method from the DiagnosticsToolbox to see more information on which constraint is causing the unit consistency issues.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Call the display_components_with_inconsistent_units() method"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This tells us that the issue lies in constraint ``c1``. If we go back and look at this constraint, we can see that it says ``v1 + v2 == 10``. ``v1`` and ``v2`` both have units of ``m`` which is consistent, but the constant in the expression (right hand side) is unitless. Thus, we need to correct this so that the right hand side has units for the constraint to be consistent.\n",
+ "\n",
+ "The cell below shows how to delete a constraint and replace it with a new one with the correct units.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "Deleting components can cause unexpected issues if something else in a model is using that component (e.g., deleting a variable which is used in a constraint). You should always be careful when deleting Pyomo components and make sure you only delete components that are not used elsewhere.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Delete the incorrect Constraint\n",
+ "m.del_component(m.c1)\n",
+ "\n",
+ "# Re-create the Constraint with the correct units\n",
+ "m.c1 = pyo.Constraint(expr=m.v1 + m.v2 == 10 * pyo.units.m)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Warning:\n",
+ "Fixing issues in models is often an iterative process requiring trial and error. You might also have some results from a model before running the diagnostics tools and the changes you make during debugging may make it difficult to replicate those results afterwards.\n",
+ " \n",
+ "It is strongly recommended that you keep a record of the changes you make at each step and why, along with a Git hash (or similar version control marker) corresponding to these changes. This will allow you see what changes and why, and give you a way to go back to previous iterations if the current approach does not work out. The IDAES documentation contains recommendations on how to keep and maintain a modeling logbook.\n",
+ "
\n",
+ "\n",
+ "Now, re-run the ``report_structural_issues()`` method and see if this change has fixed the unit consistency issue.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Call dt.report_structural_issues() in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Call the report_structural_issues() method"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The unit consistency issue has been resolved by the changes above, so now we need to look at the structural singularity. A structural singularity occurs when one sub-part of the model is over-constrained (negative degrees of freedom), which generally means another part is under-constrained (positive degrees of freedom, assuming that there are 0 degrees of freedom overall).\n",
+ "\n",
+ "The toolbox is suggesting we use the ``display_overconstrained_set()`` and ``display_underconstrained_set()`` methods to get more information on the singularity; for now, let us start with the over-constrained set.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Call dt.display_overconstrained_set() in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Call the display_overconstrained_set() method"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "From the output above, the toolbox is telling us that we have two constraints (``c2`` and ``c3``) which only contain a single unfixed variable (``v3``); thus in this part of the model we have -1 degree of freedom and the model is not well defined (structurally singular). If we go back and look at these constraints, we can see the that the constraints are:\n",
+ "\n",
+ "``c2: v3 == v4 + v5``\n",
+ "\n",
+ "``c3: 2*v3 == 3*v4 + 4*v5 + v6``\n",
+ "\n",
+ "We can see that in addition to ``v3`` these constraints actually contain 3 other variables (``v4``, ``v5`` and ``v6``), however these are all variables we fixed to get our initial zero degrees of freedom. It looks like we have either accidentally fixed one too many variables or written one too many constraints.\n",
+ "\n",
+ "For this example, let us assume that ``v4`` was not supposed to be fixed and unfix it.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Resolve the structural singularity and then call dt.report_structural_issues() in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Unfix v4\n",
+ "\n",
+ "# Then call the report_structural_issues() method again"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We can see that the over-constrained set is now empty (0 variables and 0 constraints) but the under-constrained set still has 3 variables and only 2 constraints. We can also see that there is a new warning about having 1 degree of freedom in the model, however this should not be surprising as we have just unfixed ``v4`` to resolve the over-constrained set so we have added a degree of freedom to the model.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Display the under-constrained set in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Display the under-constrained set"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Looking at the output from the ``display_underconstrained_set()`` method, we can see that we have two constraints, ``c1`` and ``c4``, which contain three unfixed variables, ``v1``, ``v2`` and ``v7``. Thus, we have one degree of freedom that needs to be addressed. To fix this, we could either fix one of the variables shown or add an additional equality constraint to the model.\n",
+ "\n",
+ "For this example let's fix ``v2`` to a value of 5 and then re-run the ``report_structural_issues()`` method.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Fix v2 to a value of 5 and then re-run dt.report_structural_issues.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Fix v2 = 5\n",
+ "\n",
+ "# Then re-run report_structural_issues() method"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The toolbox is now telling us that no warnings were found, so we have resolved all the structural issues (for now at least). The toolbox is telling us that there are also 2 non-critical issues (cautions) that we should look at; one about an unused variable and one about a variable fixed to zero. If you wish, you can look into identifying and fixing these yourself, however for this example we will move on to the next step (remember that the toolbox has methods to display more details for each of these which you can find in the documentation or from the ``help()`` function).\n",
+ "\n",
+ "For the Next Steps section, the toolbox is recommending we try to solve our model and then check for numerical issues.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Use the Pyomo SolverFactory to create an instance of IPOPT and then try to solve the model. Make sure to set \"tee=True\" as this is going to fail (and it is always good practice to review the solver logs).\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 23,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Create a solver object\n",
+ "\n",
+ "# Try to solve the model"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As hinted at above, IPOPT has returned a warning that the problem may be infeasible. Before moving on however, it is always good practice to look over the solver outputs and see what it is telling you.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "A lot of useful information is contained in the solver logs which is extremely useful when diagnosing modeling issues. Each solver has its own way of reporting output and its own specific behavior, so you will need to learn to interpret the output of each solver you use. The IDAES Documentation contains some guidance on interpreting output logs for a few common solvers.\n",
+ "
\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Call the report_numerical_issues method in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Check for numerical issues"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The ``report_numerical_issues()`` provides a summary similar to that which we saw for the structural issues. Firstly, it reports to us the Jacobian condition number for our problem which can give us an idea of how well-scaled the problem is, followed by a list of warnings, cautions and suggested next steps.\n",
+ "\n",
+ "Unsurprisingly, we are seeing a warning about a constraint with a large residual which we would expect when a solver reports a potentially infeasible problem. We are also seeing a warning about a variable with bound violations which might be contributing to the potential infeasibility.\n",
+ "\n",
+ "For the next steps, the toolbox is suggesting some new methods to get more information on these issues; let us start by looking at the constraints with large residuals.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Display the constraint with a large residual in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 27,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Display constraint with large residual"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The toolbox is telling us that the constraint which failed to converge is ``c2``, however this is generally only part of the story. Solvers work by trying to minimize the infeasibility in the model (residual of the constraints), which generally means they push any infeasibility onto the least sensitive constraint in the problem. Thus, the constraint which shows the infeasibility is often not the root cause of the problem, but only the symptom of the underlying issue.\n",
+ "\n",
+ "If we look back at the constraints, we can see that the same variables also appear in ``c3`` and that some of these have bounds, all of which could be contributing to the infeasibility. In this case the solver tried to minimize the residual in all the constraints and ended up pushing all the issues off onto ``c2``.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "When dealing with solver issues such as this, you should always remember that the obvious symptoms are often just the tip of the iceberg and that the real issue generally lies somewhere else; the challenge is tracing the symptoms back to their ultimate source.\n",
+ "
\n",
+ "\n",
+ "Next, let us take a look at the variables at or outside their bounds as well. When a solver reports an potentially infeasible solution, the most common cause is unexpected bounds violations so you should always check these first.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Display the variables with bounds violations.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 29,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Display the variables with bounds violations"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The toolbox is telling us that ``v3`` is the variable with a potential issue. It is also showing us the current value and bounds for ``v3`` as well as if it is a fixed or free variable, which will be useful for diagnosing the issues.\n",
+ "\n",
+ "We can see that ``v3`` is a free variable with bounds between 0 and 5 and a current value of 0. As ``v3`` is a free variable, this suggests that the solver has pushed the value to the bound where it cannot go any further, and this might be part of the cause of our infeasibility.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "When dealing with bounds violations you should always start by understanding why the bounds exist and what they mean - in many cases a bound indicates the range over which the model can be trusted and that going beyond this may result in unexpected behavior due to extrapolation.\n",
+ " \n",
+ "Never arbitrarily change a bound just because it is causing your model to be infeasible without understanding the consequences of this decision. Often, a bound violation is an indication that you need to re-think some of the constraints in your model to find alternatives which are valid in the actual range of values you are trying to solve for.\n",
+ "
\n",
+ "\n",
+ "For this example, let us assume that we made a mistake with the bounds on ``v3`` and set the lower bound to be -5.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Update the bounds on v3 in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 31,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Update bounds for v3"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now that we have fixed the bounds issues, we should check whether our model is now feasible. However, before we continue we should recognize that we have just made a structural change to the model. If we were not careful, this could have introduced new structural issues to the model, so we should start from the beginning just to be sure.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "In general, you should always start from the beginning of the model diagnosis workflow after you make any change to the model. Remember to also record these changes in your log book in case something unexpected happens so that you can revert any changes that cause problems.\n",
+ "
\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Check to see if there are any new structural issues in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 33,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Check for new structural issues"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Our change has not introduced any new structural issues, so we can move on and try to solve the model again.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Re-solve the model in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 35,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Re-solve the model"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "IPOPT should have returned optimal solution now, so it looks like those bounds were what was causing the model to be infeasible. At this point, the model is now solving (for the current values at least), so you might think that the model is now ready for optimization.\n",
+ "\n",
+ "However, if we look at the solver logs we can see that it took around 3 iterations for IPOPT to solve our model (depending on minor variations in computer architecture). For a model this simple, we would generally expect it to solve in only 1 iteration so there is still some room for improvement.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "You should keep in mind that just because you get an optimal solution does not mean that your model is robust and free of issues.\n",
+ " \n",
+ "You should always take the time to look over the solver logs to look for signs of trouble, even if you get an optimal solution. While you might get an optimal solution for the current state, there may be advance warning signs of issues that will cause problems later when you try to solve the model at a different state.\n",
+ "
\n",
+ "\n",
+ "Let us run the ``report_numerical_issues`` method again to see if there are any other problems we need to address.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Check for additional numerical issues in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 37,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Check for additional numerical issues"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The toolbox is not reporting any warnings which is good, however there are still 5 numerical cautions that it has identified which might be contributing to the larger than expected number of iterations. As mentioned earlier, the toolbox does not suggest methods for investigating these, but there are methods available. For example, we can look at the variable with an extreme value using the `display_variables_with_extreme_values()` method.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Check for additional information about variables with extreme values.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 39,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Display variable with extreme value"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We can see that ``v7`` is potentially causing problems due to having a very small value (on the order of magnitude of the solver tolerance). This can be especially problematic for interior point solvers like IPOPT if there is a lower bound of 0 (which there is in this case). IPOPT tries to avoid bounds and thus perturbs solutions away from these if it gets too close, which can cause convergence to be slow (or fail) if the solution lies close to the bound.\n",
+ "\n",
+ "We can address this by scaling the variable so that the value of the scaled variable is large enough that the solution is not close to the lower bound. Additionally, we should look at any constraint that ``v7`` appears in (in this case ``c4``) and ensure that those constraints are well scaled as well (so that a residual of 1e-6 is reasonable for the terms involved).\n",
+ "\n",
+ "For this case, we can set a scaling factor of 1e8 for both ``v7`` and ``c4`` as shown below. Note that we also need to apply Pyomo's scaling transformation to create a new scaled model to work with."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 41,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.scaling_factor = pyo.Suffix(direction=pyo.Suffix.EXPORT)\n",
+ "\n",
+ "m.scaling_factor[m.v7] = 1e8\n",
+ "m.scaling_factor[m.c4] = 1e8\n",
+ "\n",
+ "scaling = pyo.TransformationFactory(\"core.scale_model\")\n",
+ "scaled_model = scaling.create_using(m, rename=False)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now that we have a scaled model, we can try to solve it and hopefully see better convergence than the unscaled model.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Solve the scaled model and check to see how many iterations are required.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 42,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Solve scaled model"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As we can see, the scaled model solved in 0 iterations (indicating that it already had the right solution). However, had we done this to the unscaled model we would have found it required 2-3 iterations again due to IPOPT perturbing the initial (correct) solution away from the bounds.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "Normally in these cases we would need to map the solution from the scaled model back to the unscaled model so we can view the results. In this case, we are not actually interested in the solution so we move on with the model diagnosis.\n",
+ "
\n",
+ "\n",
+ "Now that we have fixed the scaling issues, we can go back to the ``DiagnosticsToolbox`` and see if we still have any warnings. Note however that we need to look at the scaled model now rather than the original model, so we need to create a new instance of the ``DiagnosticsToolbox`` with the scaled model as the ``model`` argument.\n",
+ "\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Create a new instance of the DiagnosticsToolbox and check the scaled model for issues.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 45,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Create a new diagnostics toolbox for scaled model\n",
+ "\n",
+ "# Report numerical issues for scaled model"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We can see that applying scaling addressed two of the cautions we had before (the variable with an extreme value and an associated large value in the model Jacobian). Whilst we were able to solve the unscaled model in this case, this is in part because it was a simple linear model. In more complex, non-linear models, scaling becomes much more important and often depends strongly on the current state of the model. That is, you can often find cases where the unscaled (or poorly scaled) model solves for a limited range of conditions but fails to solve if you move too far away for the current state. Whilst you might be able to solve the model at the current state, you should always check the solver logs and numerical cautions for advanced warning signs of scaling issues that might manifest later when you try to solve the model for a different state (e.g., during optimization).\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "By their nature, numerical issues depend on the current values of the variables in the model, and thus may remain hidden until someone tries to solve the model close to where the issue exists. For this reason, the full model diagnostics workflow contains steps to run the numerical checks across a wide range of variable values to try to ensure that no issues remain hidden. This is beyond the scope of this tutorial however.\n",
+ "
\n",
+ "\n",
+ "At this point, we have addressed all the issues that were preventing us from solving the demonstration model and so reached the end of this tutorial. For cases where we are still having trouble solving the model, we can see that the toolbox is suggesting additional methods for further debugging and these advanced features will be the focus of separate tutorials."
+ ]
+ }
+ ],
+ "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
}
\ No newline at end of file
diff --git a/idaes_examples/notebooks/docs/diagnostics/diagnostics_toolbox_solution.ipynb b/idaes_examples/notebooks/docs/diagnostics/diagnostics_toolbox_solution.ipynb
index 9ea0c9bc..688e5148 100644
--- a/idaes_examples/notebooks/docs/diagnostics/diagnostics_toolbox_solution.ipynb
+++ b/idaes_examples/notebooks/docs/diagnostics/diagnostics_toolbox_solution.ipynb
@@ -1,2104 +1,2106 @@
{
- "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": [
- "# IDAES Model Diagnostics Toolbox Tutorial\n",
- "Author: Andrew Lee \n",
- "Maintainer: Andrew Lee \n",
- "Updated: 2023-10-31 \n",
- "\n",
- "As you have likely discovered already, developing and solving models in an equation-oriented (EO) environment can be challenging and often takes a significant amount of effort. There are many pitfalls and mistakes that can be encountered when developing a model which can greatly impact the solvability and robustness of the final problem.\n",
- "\n",
- "Model diagnosis and debugging is often more of an art than a science, and it generally relies on significant experience and understanding both of general EO modeling techniques and the specific model and problem being solved. To assist with this process, IDAES has developed a model diagnostics toolbox that brings together a large number of tools for identifying potential issues in a model to help guide the user through the process of finding and resolving these issues. Note however that whilst these tools can help identify the presence of an issue, remedying the issue always requires some degree of engineering knowledge about the system being modeled, and thus it is ultimately up to the user to find a solution to the problem.\n",
- "\n",
- "This tutorial will take you through using the ``DiagnosticsToolbox`` to debug a number of issues in a simple Pyomo model and to take it from initially reporting a possible infeasible solution to returning the correct solution.\n",
- "\n",
- "To get started, the ``DiagnosticsToolbox`` can be imported from ``idaes.core.util``.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Import the DiagnosticsToolbox in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "from idaes.core.util import DiagnosticsToolbox"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "To get some information on where to start, try using the Python ``help()`` function to see the documentation for the ``DiagnosticsToolbox``.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Call `help(DiagnosticsToolbox)` to see some more information on the toolbox and some instructions on how to get started.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Call the help() function for more information"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on class DiagnosticsToolbox in module idaes.core.util.model_diagnostics:\n",
- "\n",
- "class DiagnosticsToolbox(builtins.object)\n",
- " | DiagnosticsToolbox(model: pyomo.core.base.block._BlockData, **kwargs)\n",
- " | \n",
- " | The IDAES Model DiagnosticsToolbox.\n",
- " | \n",
- " | To get started:\n",
- " | \n",
- " | 1. Create an instance of your model (this does not need to be initialized yet).\n",
- " | 2. Fix variables until you have 0 degrees of freedom. Many of these tools presume\n",
- " | a square model, and a square model should always be the foundation of any more\n",
- " | advanced model.\n",
- " | 3. Create an instance of the DiagnosticsToolbox and provide the model to debug as\n",
- " | the model argument.\n",
- " | 4. Call the ``report_structural_issues()`` method.\n",
- " | \n",
- " | Model diagnostics is an iterative process and you will likely need to run these\n",
- " | tools multiple times to resolve all issues. After making a change to your model,\n",
- " | you should always start from the beginning again to ensure the change did not\n",
- " | introduce any new issues; i.e., always start from the report_structural_issues()\n",
- " | method.\n",
- " | \n",
- " | Note that structural checks do not require the model to be initialized, thus users\n",
- " | should start with these. Numerical checks require at least a partial solution to the\n",
- " | model and should only be run once all structural issues have been resolved.\n",
- " | \n",
- " | Report methods will print a summary containing three parts:\n",
- " | \n",
- " | 1. Warnings - these are critical issues that should be resolved before continuing.\n",
- " | For each warning, a method will be suggested in the Next Steps section to get\n",
- " | additional information.\n",
- " | 2. Cautions - these are things that could be correct but could also be the source of\n",
- " | solver issues. Not all cautions need to be addressed, but users should investigate\n",
- " | each one to ensure that the behavior is correct and that they will not be the source\n",
- " | of difficulties later. Methods exist to provide more information on all cautions,\n",
- " | but these will not appear in the Next Steps section.\n",
- " | 3. Next Steps - these are recommended methods to call from the DiagnosticsToolbox to\n",
- " | get further information on warnings. If no warnings are found, this will suggest\n",
- " | the next report method to call.\n",
- " | \n",
- " | Args:\n",
- " | \n",
- " | model: model to be diagnosed. The DiagnosticsToolbox does not support indexed Blocks.\n",
- " | \n",
- " | Keyword Arguments\n",
- " | -----------------\n",
- " | variable_bounds_absolute_tolerance: float, default=0.0001\n",
- " | Absolute tolerance for considering a variable to be close to its\n",
- " | bounds.\n",
- " | \n",
- " | variable_bounds_relative_tolerance: float, default=0.0001\n",
- " | Relative tolerance for considering a variable to be close to its\n",
- " | bounds.\n",
- " | \n",
- " | variable_bounds_violation_tolerance: float, default=0\n",
- " | Absolute tolerance for considering a variable to violate its bounds.\n",
- " | Some solvers relax bounds on variables thus allowing a small violation\n",
- " | to be considered acceptable.\n",
- " | \n",
- " | constraint_residual_tolerance: float, default=1e-05\n",
- " | Absolute tolerance to use when checking constraint residuals.\n",
- " | \n",
- " | variable_large_value_tolerance: float, default=10000.0\n",
- " | Absolute tolerance for considering a value to be large.\n",
- " | \n",
- " | variable_small_value_tolerance: float, default=0.0001\n",
- " | Absolute tolerance for considering a value to be small.\n",
- " | \n",
- " | variable_zero_value_tolerance: float, default=1e-08\n",
- " | Absolute tolerance for considering a value to be near to zero.\n",
- " | \n",
- " | jacobian_large_value_caution: float, default=10000.0\n",
- " | Tolerance for raising a caution for large Jacobian values.\n",
- " | \n",
- " | jacobian_large_value_warning: float, default=100000000.0\n",
- " | Tolerance for raising a warning for large Jacobian values.\n",
- " | \n",
- " | jacobian_small_value_caution: float, default=0.0001\n",
- " | Tolerance for raising a caution for small Jacobian values.\n",
- " | \n",
- " | jacobian_small_value_warning: float, default=1e-08\n",
- " | Tolerance for raising a warning for small Jacobian values.\n",
- " | \n",
- " | warn_for_evaluation_error_at_bounds: bool, default=True\n",
- " | If False, warnings will not be generated for things like log(x) with x\n",
- " | >= 0\n",
- " | \n",
- " | Methods defined here:\n",
- " | \n",
- " | __init__(self, model: pyomo.core.base.block._BlockData, **kwargs)\n",
- " | Initialize self. See help(type(self)) for accurate signature.\n",
- " | \n",
- " | assert_no_numerical_warnings(self)\n",
- " | Checks for numerical warnings in the model and raises an AssertionError\n",
- " | if any are found.\n",
- " | \n",
- " | Raises:\n",
- " | AssertionError if any warnings are identified by numerical analysis.\n",
- " | \n",
- " | assert_no_structural_warnings(self)\n",
- " | Checks for structural warnings in the model and raises an AssertionError\n",
- " | if any are found.\n",
- " | \n",
- " | Raises:\n",
- " | AssertionError if any warnings are identified by structural analysis.\n",
- " | \n",
- " | display_components_with_inconsistent_units(self, stream=None)\n",
- " | Prints a list of all Constraints, Expressions and Objectives in the\n",
- " | model with inconsistent units of measurement.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_constraints_with_extreme_jacobians(self, stream=None)\n",
- " | Prints the constraints associated with rows in the Jacobian with extreme\n",
- " | L2 norms. This often indicates poorly scaled constraints.\n",
- " | \n",
- " | Tolerances can be set via the DiagnosticsToolbox config.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the output to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_constraints_with_large_residuals(self, stream=None)\n",
- " | Prints a list of Constraints with residuals greater than a specified tolerance.\n",
- " | Tolerance can be set in the class configuration options.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_external_variables(self, stream=None)\n",
- " | Prints a list of variables that appear within activated Constraints in the\n",
- " | model but are not contained within the model themselves.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_extreme_jacobian_entries(self, stream=None)\n",
- " | Prints variables and constraints associated with entries in the Jacobian with extreme\n",
- " | values. This can be indicative of poor scaling, especially for isolated terms (e.g.\n",
- " | variables which appear only in one term of a single constraint).\n",
- " | \n",
- " | Tolerances can be set via the DiagnosticsToolbox config.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the output to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_overconstrained_set(self, stream=None)\n",
- " | Prints the variables and constraints in the over-constrained sub-problem\n",
- " | from a Dulmage-Mendelsohn partitioning.\n",
- " | \n",
- " | This can be used to identify the over-defined part of a model and thus\n",
- " | where constraints must be removed or variables unfixed.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_potential_evaluation_errors(self, stream=None)\n",
- " | Prints constraints that may be prone to evaluation errors\n",
- " | (e.g., log of a negative number) based on variable bounds.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the output to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_underconstrained_set(self, stream=None)\n",
- " | Prints the variables and constraints in the under-constrained sub-problem\n",
- " | from a Dulmage-Mendelsohn partitioning.\n",
- " | \n",
- " | This can be used to identify the under-defined part of a model and thus\n",
- " | where additional information (fixed variables or constraints) are required.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_unused_variables(self, stream=None)\n",
- " | Prints a list of variables that do not appear in any activated Constraints.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_variables_at_or_outside_bounds(self, stream=None)\n",
- " | Prints a list of variables with values that fall at or outside the bounds\n",
- " | on the variable.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_variables_fixed_to_zero(self, stream=None)\n",
- " | Prints a list of variables that are fixed to an absolute value of 0.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_variables_near_bounds(self, stream=None)\n",
- " | Prints a list of variables with values close to their bounds. Tolerance can\n",
- " | be set in the class configuration options.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_variables_with_extreme_jacobians(self, stream=None)\n",
- " | Prints the variables associated with columns in the Jacobian with extreme\n",
- " | L2 norms. This often indicates poorly scaled variables.\n",
- " | \n",
- " | Tolerances can be set via the DiagnosticsToolbox config.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the output to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_variables_with_extreme_values(self, stream=None)\n",
- " | Prints a list of variables with extreme values.\n",
- " | \n",
- " | Tolerances can be set in the class configuration options.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_variables_with_none_value(self, stream=None)\n",
- " | Prints a list of variables with a value of None.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_variables_with_value_near_zero(self, stream=None)\n",
- " | Prints a list of variables with a value close to zero. The tolerance\n",
- " | for determining what is close to zero can be set in the class configuration\n",
- " | options.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | get_dulmage_mendelsohn_partition(self)\n",
- " | Performs a Dulmage-Mendelsohn partitioning on the model and returns\n",
- " | the over- and under-constrained sub-problems.\n",
- " | \n",
- " | Returns:\n",
- " | list-of-lists variables in each independent block of the under-constrained set\n",
- " | list-of-lists constraints in each independent block of the under-constrained set\n",
- " | list-of-lists variables in each independent block of the over-constrained set\n",
- " | list-of-lists constraints in each independent block of the over-constrained set\n",
- " | \n",
- " | prepare_degeneracy_hunter(self, **kwargs)\n",
- " | Create an instance of the DegeneracyHunter and store as self.degeneracy_hunter.\n",
- " | \n",
- " | After creating an instance of the toolbox, call\n",
- " | report_irreducible_degenerate_sets.\n",
- " | \n",
- " | Returns:\n",
- " | \n",
- " | Instance of DegeneracyHunter\n",
- " | \n",
- " | Keyword Arguments\n",
- " | -----------------\n",
- " | solver: str, default='scip'\n",
- " | MILP solver to use for finding irreducible degenerate sets.\n",
- " | \n",
- " | solver_options: optional\n",
- " | Options to pass to MILP solver.\n",
- " | \n",
- " | M: float, default=100000.0\n",
- " | Maximum value for nu in MILP models.\n",
- " | \n",
- " | m_small: float, default=1e-05\n",
- " | Smallest value for nu to be considered non-zero in MILP models.\n",
- " | \n",
- " | trivial_constraint_tolerance: float, default=1e-06\n",
- " | Tolerance for identifying non-zero rows in Jacobian.\n",
- " | \n",
- " | prepare_svd_toolbox(self, **kwargs)\n",
- " | Create an instance of the SVDToolbox and store as self.svd_toolbox.\n",
- " | \n",
- " | After creating an instance of the toolbox, call\n",
- " | display_underdetermined_variables_and_constraints().\n",
- " | \n",
- " | Returns:\n",
- " | \n",
- " | Instance of SVDToolbox\n",
- " | \n",
- " | Keyword Arguments\n",
- " | -----------------\n",
- " | number_of_smallest_singular_values: PositiveInt, optional\n",
- " | Number of smallest singular values to compute\n",
- " | \n",
- " | svd_callback: svd_callback_validator, default=\n",
- " | Callback to SVD method of choice (default = svd_dense). Callbacks\n",
- " | should take the Jacobian and number of singular values to compute as\n",
- " | options, plus any method specific arguments, and should return the u,\n",
- " | s and v matrices as numpy arrays.\n",
- " | \n",
- " | svd_callback_arguments: dict, optional\n",
- " | Optional arguments to pass to SVD callback (default = None)\n",
- " | \n",
- " | singular_value_tolerance: float, default=1e-06\n",
- " | Tolerance for defining a small singular value\n",
- " | \n",
- " | size_cutoff_in_singular_vector: float, default=0.1\n",
- " | Size below which to ignore constraints and variables in the singular\n",
- " | vector\n",
- " | \n",
- " | report_numerical_issues(self, stream=None)\n",
- " | Generates a summary report of any numerical issues identified in the model provided\n",
- " | and suggest next steps for debugging model.\n",
- " | \n",
- " | Numerical checks should only be performed once all structural issues have been resolved,\n",
- " | and require that at least a partial solution to the model is available.\n",
- " | \n",
- " | Args:\n",
- " | stream: I/O object to write report to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | report_structural_issues(self, stream=None)\n",
- " | Generates a summary report of any structural issues identified in the model provided\n",
- " | and suggests next steps for debugging the model.\n",
- " | \n",
- " | This should be the first method called when debugging a model and after any change\n",
- " | is made to the model. These checks can be run before trying to initialize and solve\n",
- " | the model.\n",
- " | \n",
- " | Args:\n",
- " | stream: I/O object to write report to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | ----------------------------------------------------------------------\n",
- " | Readonly properties defined here:\n",
- " | \n",
- " | model\n",
- " | Model currently being diagnosed.\n",
- " | \n",
- " | ----------------------------------------------------------------------\n",
- " | Data descriptors defined here:\n",
- " | \n",
- " | __dict__\n",
- " | dictionary for instance variables (if defined)\n",
- " | \n",
- " | __weakref__\n",
- " | list of weak references to the object (if defined)\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(DiagnosticsToolbox)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The ``help()`` function gives us a lot of information on the ``DiagnosticsToolbox`` and all the methods that it supports (and there are many). However, the important part to start with are the four steps outlined at the top of the doc string that tell us how to get started.\n",
- "\n",
- "Firstly, we need a model to test (and, for this tutorial at least, one that has a wide range of issues that we need to fix before it will solve). We then also need to fix some variables so that we have 0 degrees of freedom in our model. Whilst our ultimate goal is generally optimization (and thus a system with 1 or more degrees of freedom), all models conceptually derive from a square model representing a nominal state. If this nominal state is not well-posed, then any issues present will also be present in the resulting optimization (even if adding degrees of freedom means that the model is now easier to solve).\n",
- "\n",
- "The cell below contains a demonstration model for this tutorial that contains a number of issues that we will resolve using the ``DiagnosticsToolbox``."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [],
- "source": [
- "import pyomo.environ as pyo\n",
- "\n",
- "m = pyo.ConcreteModel()\n",
- "\n",
- "m.v1 = pyo.Var(units=pyo.units.m)\n",
- "m.v2 = pyo.Var(units=pyo.units.m)\n",
- "m.v3 = pyo.Var(bounds=(0, 5))\n",
- "m.v4 = pyo.Var()\n",
- "m.v5 = pyo.Var(bounds=(0, 10))\n",
- "m.v6 = pyo.Var()\n",
- "m.v7 = pyo.Var(units=pyo.units.m, bounds=(0, 1)) # Poorly scaled variable with lower bound\n",
- "m.v8 = pyo.Var() # unused variable\n",
- "\n",
- "m.c1 = pyo.Constraint(expr=m.v1 + m.v2 == 10) # Unit consistency issue\n",
- "m.c2 = pyo.Constraint(expr=m.v3 == m.v4 + m.v5)\n",
- "m.c3 = pyo.Constraint(expr=2*m.v3 == 3*m.v4 + 4*m.v5 + m.v6)\n",
- "m.c4 = pyo.Constraint(expr=m.v7 == 1e-8*m.v1) # Poorly scaled constraint\n",
- "\n",
- "m.v4.fix(2)\n",
- "m.v5.fix(2)\n",
- "m.v6.fix(0)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Next, the instructions tell us to create an instance of the ``DiagnosticsToolbox`` and to pass the model we wish to examine as an argument.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Create an instance of the DiagnosticsToolbox: dt = DiagnosticsToolbox(m)\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Create an instance of the Diagnostics Toolbox"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "dt = DiagnosticsToolbox(m)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Finally, the instructions tell us to run the ``report_structural_issues()`` method. Structural issues represent issues that exist solely in the form of the model equations and thus do not depend on the current value of any of the variables. This is useful as it means we can check for these before we even call a solver, which can be critical as sometimes these issues will cause a solver to fail without providing a useful solution.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Call dt.report_structural_issues() in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Call the report_strucutral_issues() method"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Model Statistics\n",
- "\n",
- " Activated Blocks: 1 (Deactivated: 0)\n",
- " Free Variables in Activated Constraints: 4 (External: 0)\n",
- " Free Variables with only lower bounds: 0\n",
- " Free Variables with only upper bounds: 0\n",
- " Free Variables with upper and lower bounds: 2\n",
- " Fixed Variables in Activated Constraints: 3 (External: 0)\n",
- " Activated Equality Constraints: 4 (Deactivated: 0)\n",
- " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
- " Activated Objectives: 0 (Deactivated: 0)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "2 WARNINGS\n",
- "\n",
- " WARNING: 1 Component with inconsistent units\n",
- " WARNING: Structural singularity found\n",
- " Under-Constrained Set: 3 variables, 2 constraints\n",
- " Over-Constrained Set: 1 variables, 2 constraints\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "2 Cautions\n",
- "\n",
- " Caution: 1 variable fixed to 0\n",
- " Caution: 1 unused variable (0 fixed)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "Suggested next steps:\n",
- "\n",
- " display_components_with_inconsistent_units()\n",
- " display_underconstrained_set()\n",
- " display_overconstrained_set()\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.report_structural_issues()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Looking at the output from the ``report_structural_issues()`` method, we can see that it provides a fairly short summary containing 4 sections.\n",
- "\n",
- "1. The first section is a summary of the size of the model, indicating things like the number of variables and constraints. The size of the model is often important for judging how difficult it will be to solve, and this information can also be useful for comparison to what is being sent to the solver. Most solvers will report the size of the model in their output logs, and if there is a difference between what is reported here and by the solver, then you should probably look into what is happening. This section also notes some things such as if you have any deactivated Blocks, Constraints or Objectives, or if you have variables which appear in the constraints that are not part of the model; these are not necessarily wrong but it is easy to have accidentally deactivated something you did not intend to so you should always check to see that these are expected.\n",
- "\n",
- "2. The second section provides a summary of any critical structural issues that were found - in this case we can see that there are 2 warnings we are going to need to look into. Warnings represent issues that need to be addressed before moving on as these will likely cause the solver to fail or give an incorrect answer.\n",
- "\n",
- "3. The third section lists a summary of any cautions that are found. Cautions represent issues that may or may not be problematic; in many cases these might be expected behaviors or borderline issues. However, these could also represent conceptual issues that should be addressed, so users should take the time to investigate these and determine if they need to be fixed or not.\n",
- "\n",
- "4. Finally, there is a section that suggests the next steps to take to help guide you through the model diagnosis process. If any warnings were identified, this section will list methods that can help you get more information on each specific problem, and if no warnings are found then it will guide you onto the next step in the model diagnosis workflow.\n",
- "\n",
- "**Note:** there are methods available to help investigate cautions as well, but these will not show up in the next steps in order to avoid cluttering the output. You can get more information on the available methods for investigating cautions via the documentation or ``help()`` function.\n",
- "\n",
- "In our current model, we have 2 critical issues (warnings) that we need to look into and resolve. The order in which we resolve these will generally not matter, but be aware that these can often be interrelated - fixing one warning might resolve other warnings as well (or create new ones), and sometimes you will need to look at multiple issues together to find the overall root cause.\n",
- "\n",
- "To start with, let us look at the unit consistency issue. From the \"Next Steps\" section above, the toolbox is suggesting we run the ``display_components_with_inconsistent_units()`` method for more information.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Call the `display_components_with_inconsistent_units()` method from the DiagnosticsToolbox to see more information on which constraint is causing the unit consistency issues.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Call the display_components_with_inconsistent_units() method"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 11,
- "metadata": {
- "scrolled": false,
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "The following component(s) have unit consistency issues:\n",
- "\n",
- " c1\n",
- "\n",
- "For more details on unit inconsistencies, import the assert_units_consistent method\n",
- "from pyomo.util.check_units\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.display_components_with_inconsistent_units()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "This tells us that the issue lies in constraint ``c1``. If we go back and look at this constraint, we can see that it says ``v1 + v2 == 10``. ``v1`` and ``v2`` both have units of ``m`` which is consistent, but the constant in the expression (right hand side) is unitless. Thus, we need to correct this so that the right hand side has units for the constraint to be consistent.\n",
- "\n",
- "The cell below shows how to delete a constraint and replace it with a new one with the correct units.\n",
- "\n",
- "\n",
- "Warning:\n",
- "Deleting components can cause unexpected issues if something else in a model is using that component (e.g., deleting a variable which is used in a constraint). You should always be careful when deleting Pyomo components and make sure you only delete components that are not used elsewhere.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 12,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Delete the incorrect Constraint\n",
- "m.del_component(m.c1)\n",
- "\n",
- "# Re-create the Constraint with the correct units\n",
- "m.c1 = pyo.Constraint(expr=m.v1 + m.v2 == 10*pyo.units.m)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Warning:\n",
- "Fixing issues in models is often an iterative process requiring trial and error. You might also have some results from a model before running the diagnostics tools and the changes you make during debugging may make it difficult to replicate those results afterwards.\n",
- " \n",
- "It is strongly recommended that you keep a record of the changes you make at each step and why, along with a Git hash (or similar version control marker) corresponding to these changes. This will allow you see what changes and why, and give you a way to go back to previous iterations if the current approach does not work out. The IDAES documentation contains recommendations on how to keep and maintain a modeling logbook.\n",
- "
\n",
- "\n",
- "Now, re-run the ``report_structural_issues()`` method and see if this change has fixed the unit consistency issue.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Call dt.report_structural_issues() in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 13,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Call the report_structural_issues() method"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 14,
- "metadata": {
- "scrolled": true,
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Model Statistics\n",
- "\n",
- " Activated Blocks: 1 (Deactivated: 0)\n",
- " Free Variables in Activated Constraints: 4 (External: 0)\n",
- " Free Variables with only lower bounds: 0\n",
- " Free Variables with only upper bounds: 0\n",
- " Free Variables with upper and lower bounds: 2\n",
- " Fixed Variables in Activated Constraints: 3 (External: 0)\n",
- " Activated Equality Constraints: 4 (Deactivated: 0)\n",
- " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
- " Activated Objectives: 0 (Deactivated: 0)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "1 WARNINGS\n",
- "\n",
- " WARNING: Structural singularity found\n",
- " Under-Constrained Set: 3 variables, 2 constraints\n",
- " Over-Constrained Set: 1 variables, 2 constraints\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "2 Cautions\n",
- "\n",
- " Caution: 1 variable fixed to 0\n",
- " Caution: 1 unused variable (0 fixed)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "Suggested next steps:\n",
- "\n",
- " display_underconstrained_set()\n",
- " display_overconstrained_set()\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.report_structural_issues()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The unit consistency issue has been resolved by the changes above, so now we need to look at the structural singularity. A structural singularity occurs when one sub-part of the model is over-constrained (negative degrees of freedom), which generally means another part is under-constrained (positive degrees of freedom, assuming that there are 0 degrees of freedom overall).\n",
- "\n",
- "The toolbox is suggesting we use the ``display_overconstrained_set()`` and ``display_underconstrained_set()`` methods to get more information on the singularity; for now, let us start with the over-constrained set.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Call dt.display_overconstrained_set() in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 15,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Call the display_overconstrained_set() method"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 16,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Dulmage-Mendelsohn Over-Constrained Set\n",
- "\n",
- " Independent Block 0:\n",
- "\n",
- " Variables:\n",
- "\n",
- " v3\n",
- "\n",
- " Constraints:\n",
- "\n",
- " c2\n",
- " c3\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.display_overconstrained_set()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "From the output above, the toolbox is telling us that we have two constraints (``c2`` and ``c3``) which only contain a single unfixed variable (``v3``); thus in this part of the model we have -1 degree of freedom and the model is not well defined (structurally singular). If we go back and look at these constraints, we can see the that the constraints are:\n",
- "\n",
- "``c2: v3 == v4 + v5``\n",
- "\n",
- "``c3: 2*v3 == 3*v4 + 4*v5 + v6``\n",
- "\n",
- "We can see that in addition to ``v3`` these constraints actually contain 3 other variables (``v4``, ``v5`` and ``v6``), however these are all variables we fixed to get our initial zero degrees of freedom. It looks like we have either accidentally fixed one too many variables or written one too many constraints.\n",
- "\n",
- "For this example, let us assume that ``v4`` was not supposed to be fixed and unfix it.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Resolve the structural singularity and then call dt.report_structural_issues() in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 17,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Unfix v4\n",
- "\n",
- "# Then call the report_structural_issues() method again"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 18,
- "metadata": {
- "scrolled": true,
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Model Statistics\n",
- "\n",
- " Activated Blocks: 1 (Deactivated: 0)\n",
- " Free Variables in Activated Constraints: 5 (External: 0)\n",
- " Free Variables with only lower bounds: 0\n",
- " Free Variables with only upper bounds: 0\n",
- " Free Variables with upper and lower bounds: 2\n",
- " Fixed Variables in Activated Constraints: 2 (External: 0)\n",
- " Activated Equality Constraints: 4 (Deactivated: 0)\n",
- " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
- " Activated Objectives: 0 (Deactivated: 0)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "2 WARNINGS\n",
- "\n",
- " WARNING: 1 Degree of Freedom\n",
- " WARNING: Structural singularity found\n",
- " Under-Constrained Set: 3 variables, 2 constraints\n",
- " Over-Constrained Set: 0 variables, 0 constraints\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "2 Cautions\n",
- "\n",
- " Caution: 1 variable fixed to 0\n",
- " Caution: 1 unused variable (0 fixed)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "Suggested next steps:\n",
- "\n",
- " display_underconstrained_set()\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "m.v4.unfix()\n",
- "\n",
- "dt.report_structural_issues()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We can see that the over-constrained set is now empty (0 variables and 0 constraints) but the under-constrained set still has 3 variables and only 2 constraints. We can also see that there is a new warning about having 1 degree of freedom in the model, however this should not be surprising as we have just unfixed ``v4`` to resolve the over-constrained set so we have added a degree of freedom to the model.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Display the under-constrained set in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 19,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Display the under-constrained set"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 20,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Dulmage-Mendelsohn Under-Constrained Set\n",
- "\n",
- " Independent Block 0:\n",
- "\n",
- " Variables:\n",
- "\n",
- " v2\n",
- " v1\n",
- " v7\n",
- "\n",
- " Constraints:\n",
- "\n",
- " c1\n",
- " c4\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.display_underconstrained_set()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Looking at the output from the ``display_underconstrained_set()`` method, we can see that we have two constraints, ``c1`` and ``c4``, which contain three unfixed variables, ``v1``, ``v2`` and ``v7``. Thus, we have one degree of freedom that needs to be addressed. To fix this, we could either fix one of the variables shown or add an additional equality constraint to the model.\n",
- "\n",
- "For this example let's fix ``v2`` to a value of 5 and then re-run the ``report_structural_issues()`` method.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Fix v2 to a value of 5 and then re-run dt.report_structural_issues.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 21,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Fix v2 = 5\n",
- "\n",
- "# Then re-run report_structural_issues() method"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 22,
- "metadata": {
- "scrolled": true,
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Model Statistics\n",
- "\n",
- " Activated Blocks: 1 (Deactivated: 0)\n",
- " Free Variables in Activated Constraints: 4 (External: 0)\n",
- " Free Variables with only lower bounds: 0\n",
- " Free Variables with only upper bounds: 0\n",
- " Free Variables with upper and lower bounds: 2\n",
- " Fixed Variables in Activated Constraints: 3 (External: 0)\n",
- " Activated Equality Constraints: 4 (Deactivated: 0)\n",
- " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
- " Activated Objectives: 0 (Deactivated: 0)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "0 WARNINGS\n",
- "\n",
- " No warnings found!\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "2 Cautions\n",
- "\n",
- " Caution: 1 variable fixed to 0\n",
- " Caution: 1 unused variable (0 fixed)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "Suggested next steps:\n",
- "\n",
- " Try to initialize/solve your model and then call report_numerical_issues()\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "m.v2.fix(5)\n",
- "\n",
- "dt.report_structural_issues()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The toolbox is now telling us that no warnings were found, so we have resolved all the structural issues (for now at least). The toolbox is telling us that there are also 2 non-critical issues (cautions) that we should look at; one about an unused variable and one about a variable fixed to zero. If you wish, you can look into identifying and fixing these yourself, however for this example we will move on to the next step (remember that the toolbox has methods to display more details for each of these which you can find in the documentation or from the ``help()`` function).\n",
- "\n",
- "For the Next Steps section, the toolbox is recommending we try to solve our model and then check for numerical issues.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Use the Pyomo SolverFactory to create an instance of IPOPT and then try to solve the model. Make sure to set \"tee=True\" as this is going to fail (and it is always good practice to review the solver logs).\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 23,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Create a solver object\n",
- "\n",
- "# Try to solve the model"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 24,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "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...: 7\n",
- "Number of nonzeros in inequality constraint Jacobian.: 0\n",
- "Number of nonzeros in Lagrangian Hessian.............: 0\n",
- "\n",
- "Total number of variables............................: 4\n",
- " variables with only lower bounds: 0\n",
- " variables with lower and upper bounds: 2\n",
- " variables with only upper bounds: 0\n",
- "Total number of equality constraints.................: 4\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 1.40e+01 0.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
- " 1 0.0000000e+00 1.39e+01 1.50e+02 -1.0 6.00e+00 - 7.16e-01 4.93e-03h 1\n",
- " 2 0.0000000e+00 1.39e+01 3.03e+06 -1.0 5.97e+00 - 1.00e+00 4.95e-05h 1\n",
- " 3r 0.0000000e+00 1.39e+01 1.00e+03 1.1 0.00e+00 - 0.00e+00 2.47e-07R 2\n",
- " 4r 0.0000000e+00 4.19e+00 9.42e+02 1.1 3.50e+03 - 4.02e-01 3.37e-03f 1\n",
- " 5r 0.0000000e+00 2.12e+00 8.72e+02 1.1 5.89e+01 - 4.35e-01 7.06e-02f 1\n",
- " 6r 0.0000000e+00 6.74e-01 6.06e+02 1.1 5.29e+00 - 9.93e-03 3.98e-01f 1\n",
- " 7r 0.0000000e+00 6.80e-01 3.14e+02 0.4 2.05e-01 - 1.00e+00 1.03e-01f 1\n",
- " 8r 0.0000000e+00 6.69e-01 2.78e-05 0.4 2.58e-02 - 1.00e+00 1.00e+00f 1\n",
- " 9r 0.0000000e+00 6.67e-01 7.56e+00 -1.7 8.13e-03 - 9.93e-01 9.96e-01f 1\n",
- "iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n",
- " 10r 0.0000000e+00 6.67e-01 2.23e-07 -1.7 4.13e-05 - 1.00e+00 1.00e+00f 1\n",
- " 11r 0.0000000e+00 6.67e-01 6.73e-01 -3.7 6.61e-05 - 1.00e+00 1.00e+00f 1\n",
- " 12r 0.0000000e+00 6.67e-01 1.91e-09 -3.7 1.48e-09 - 1.00e+00 1.00e+00h 1\n",
- " 13r 0.0000000e+00 6.67e-01 2.69e+00 -8.4 5.74e-07 - 1.00e+00 9.26e-01f 1\n",
- " 14r 0.0000000e+00 6.67e-01 7.65e+01 -8.4 4.23e-08 - 8.68e-01 1.00e+00f 1\n",
- "\n",
- "Number of Iterations....: 14\n",
- "\n",
- " (scaled) (unscaled)\n",
- "Objective...............: 0.0000000000000000e+00 0.0000000000000000e+00\n",
- "Dual infeasibility......: 3.2644919411246030e-04 3.2644919411246030e-04\n",
- "Constraint violation....: 6.6666666333656233e-01 6.6666666333656233e-01\n",
- "Complementarity.........: 4.6615546565561981e-09 4.6615546565561981e-09\n",
- "Overall NLP error.......: 6.6666666333656233e-01 6.6666666333656233e-01\n",
- "\n",
- "\n",
- "Number of objective function evaluations = 18\n",
- "Number of objective gradient evaluations = 5\n",
- "Number of equality constraint evaluations = 18\n",
- "Number of inequality constraint evaluations = 0\n",
- "Number of equality constraint Jacobian evaluations = 17\n",
- "Number of inequality constraint Jacobian evaluations = 0\n",
- "Number of Lagrangian Hessian evaluations = 15\n",
- "Total CPU secs in IPOPT (w/o function evaluations) = 0.003\n",
- "Total CPU secs in NLP function evaluations = 0.000\n",
- "\n",
- "EXIT: Converged to a point of local infeasibility. Problem may be infeasible.\n",
- "WARNING: Loading a SolverResults object with a warning status into\n",
- "model.name=\"unknown\";\n",
- " - termination condition: infeasible\n",
- " - message from solver: Ipopt 3.13.2\\x3a Converged to a locally infeasible\n",
- " point. Problem may be infeasible.\n"
- ]
- },
- {
- "data": {
- "text/plain": [
- "{'Problem': [{'Lower bound': -inf, 'Upper bound': inf, 'Number of objectives': 1, 'Number of constraints': 4, 'Number of variables': 4, 'Sense': 'unknown'}], 'Solver': [{'Status': 'warning', 'Message': 'Ipopt 3.13.2\\\\x3a Converged to a locally infeasible point. Problem may be infeasible.', 'Termination condition': 'infeasible', 'Id': 200, 'Error rc': 0, 'Time': 0.007064104080200195}], 'Solution': [OrderedDict([('number of solutions', 0), ('number of solutions displayed', 0)])]}"
- ]
- },
- "execution_count": 24,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "solver = pyo.SolverFactory('ipopt')\n",
- "solver.solve(m, tee=True)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "As hinted at above, IPOPT has returned a warning that the problem may be infeasible. Before moving on however, it is always good practice to look over the solver outputs and see what it is telling you.\n",
- "\n",
- "\n",
- "Warning:\n",
- "A lot of useful information is contained in the solver logs which is extremely useful when diagnosing modeling issues. Each solver has its own way of reporting output and its own specific behavior, so you will need to learn to interpret the output of each solver you use. The IDAES Documentation contains some guidance on interpreting output logs for a few common solvers.\n",
- "
\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Call the report_numerical_issues method in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 25,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Check for numerical issues"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 26,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Model Statistics\n",
- "\n",
- " Jacobian Condition Number: 1.700E+01\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "2 WARNINGS\n",
- "\n",
- " WARNING: 1 Constraint with large residuals (>1.0E-05)\n",
- " WARNING: 1 Variable at or outside bounds (tol=0.0E+00)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "5 Cautions\n",
- "\n",
- " Caution: 2 Variables with value close to their bounds (abs=1.0E-04, rel=1.0E-04)\n",
- " Caution: 2 Variables with value close to zero (tol=1.0E-08)\n",
- " Caution: 1 Variable with extreme value (<1.0E-04 or >1.0E+04)\n",
- " Caution: 1 Variable with None value\n",
- " Caution: 1 extreme Jacobian Entry (<1.0E-04 or >1.0E+04)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "Suggested next steps:\n",
- "\n",
- " display_constraints_with_large_residuals()\n",
- " display_variables_at_or_outside_bounds()\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.report_numerical_issues()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The ``report_numerical_issues()`` provides a summary similar to that which we saw for the structural issues. Firstly, it reports to us the Jacobian condition number for our problem which can give us an idea of how well-scaled the problem is, followed by a list of warnings, cautions and suggested next steps.\n",
- "\n",
- "Unsurprisingly, we are seeing a warning about a constraint with a large residual which we would expect when a solver reports a potentially infeasible problem. We are also seeing a warning about a variable with bound violations which might be contributing to the potential infeasibility.\n",
- "\n",
- "For the next steps, the toolbox is suggesting some new methods to get more information on these issues; let us start by looking at the constraints with large residuals.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Display the constraint with a large residual in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 27,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Display constraint with large residual"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 28,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "The following constraint(s) have large residuals (>1.0E-05):\n",
- "\n",
- " c2: 6.66667E-01\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.display_constraints_with_large_residuals()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The toolbox is telling us that the constraint which failed to converge is ``c2``, however this is generally only part of the story. Solvers work by trying to minimize the infeasibility in the model (residual of the constraints), which generally means they push any infeasibility onto the least sensitive constraint in the problem. Thus, the constraint which shows the infeasibility is often not the root cause of the problem, but only the symptom of the underlying issue.\n",
- "\n",
- "If we look back at the constraints, we can see that the same variables also appear in ``c3`` and that some of these have bounds, all of which could be contributing to the infeasibility. In this case the solver tried to minimize the residual in all the constraints and ended up pushing all the issues off onto ``c2``.\n",
- "\n",
- "\n",
- "Warning:\n",
- "When dealing with solver issues such as this, you should always remember that the obvious symptoms are often just the tip of the iceberg and that the real issue generally lies somewhere else; the challenge is tracing the symptoms back to their ultimate source.\n",
- "
\n",
- "\n",
- "Next, let us take a look at the variables at or outside their bounds as well. When a solver reports an potentially infeasible solution, the most common cause is unexpected bounds violations so you should always check these first.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Display the variables with bounds violations.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 29,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Display the variables with bounds violations"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 30,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "The following variable(s) have values at or outside their bounds (tol=0.0E+00):\n",
- "\n",
- " v3 (free): value=0.0 bounds=(0, 5)\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.display_variables_at_or_outside_bounds()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The toolbox is telling us that ``v3`` is the variable with a potential issue. It is also showing us the current value and bounds for ``v3`` as well as if it is a fixed or free variable, which will be useful for diagnosing the issues.\n",
- "\n",
- "We can see that ``v3`` is a free variable with bounds between 0 and 5 and a current value of 0. As ``v3`` is a free variable, this suggests that the solver has pushed the value to the bound where it cannot go any further, and this might be part of the cause of our infeasibility.\n",
- "\n",
- "\n",
- "Warning:\n",
- "When dealing with bounds violations you should always start by understanding why the bounds exist and what they mean - in many cases a bound indicates the range over which the model can be trusted and that going beyond this may result in unexpected behavior due to extrapolation.\n",
- " \n",
- "Never arbitrarily change a bound just because it is causing your model to be infeasible without understanding the consequences of this decision. Often, a bound violation is an indication that you need to re-think some of the constraints in your model to find alternatives which are valid in the actual range of values you are trying to solve for.\n",
- "
\n",
- "\n",
- "For this example, let us assume that we made a mistake with the bounds on ``v3`` and set the lower bound to be -5.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Update the bounds on v3 in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 31,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Update bounds for v3"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 32,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "m.v3.setlb(-5)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Now that we have fixed the bounds issues, we should check whether our model is now feasible. However, before we continue we should recognize that we have just made a structural change to the model. If we were not careful, this could have introduced new structural issues to the model, so we should start from the beginning just to be sure.\n",
- "\n",
- "\n",
- "Warning:\n",
- "In general, you should always start from the beginning of the model diagnosis workflow after you make any change to the model. Remember to also record these changes in your log book in case something unexpected happens so that you can revert any changes that cause problems.\n",
- "
\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Check to see if there are any new structural issues in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 33,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Check for new strucutral issues"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 34,
- "metadata": {
- "scrolled": true,
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Model Statistics\n",
- "\n",
- " Activated Blocks: 1 (Deactivated: 0)\n",
- " Free Variables in Activated Constraints: 4 (External: 0)\n",
- " Free Variables with only lower bounds: 0\n",
- " Free Variables with only upper bounds: 0\n",
- " Free Variables with upper and lower bounds: 2\n",
- " Fixed Variables in Activated Constraints: 3 (External: 0)\n",
- " Activated Equality Constraints: 4 (Deactivated: 0)\n",
- " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
- " Activated Objectives: 0 (Deactivated: 0)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "0 WARNINGS\n",
- "\n",
- " No warnings found!\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "2 Cautions\n",
- "\n",
- " Caution: 1 variable fixed to 0\n",
- " Caution: 1 unused variable (0 fixed)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "Suggested next steps:\n",
- "\n",
- " Try to initialize/solve your model and then call report_numerical_issues()\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.report_structural_issues()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Our change has not introduced any new structural issues, so we can move on and try to solve the model again.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Re-solve the model in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 35,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Re-solve the model"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 36,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "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...: 7\n",
- "Number of nonzeros in inequality constraint Jacobian.: 0\n",
- "Number of nonzeros in Lagrangian Hessian.............: 0\n",
- "\n",
- "Total number of variables............................: 4\n",
- " variables with only lower bounds: 0\n",
- " variables with lower and upper bounds: 2\n",
- " variables with only upper bounds: 0\n",
- "Total number of equality constraints.................: 4\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 6.67e-01 0.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
- " 1 0.0000000e+00 6.66e-03 2.97e+00 -1.0 2.00e+00 - 7.17e-01 9.90e-01h 1\n",
- " 2 0.0000000e+00 6.27e-05 9.38e+00 -1.0 2.00e-02 - 1.00e+00 9.91e-01h 1\n",
- " 3 0.0000000e+00 8.88e-16 1.13e-12 -1.0 1.88e-04 - 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....: 8.8817841970012523e-16 8.8817841970012523e-16\n",
- "Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
- "Overall NLP error.......: 8.8817841970012523e-16 8.8817841970012523e-16\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"
- ]
- },
- {
- "data": {
- "text/plain": [
- "{'Problem': [{'Lower bound': -inf, 'Upper bound': inf, 'Number of objectives': 1, 'Number of constraints': 4, 'Number of variables': 4, 'Sense': 'unknown'}], 'Solver': [{'Status': 'ok', 'Message': 'Ipopt 3.13.2\\\\x3a Optimal Solution Found', 'Termination condition': 'optimal', 'Id': 0, 'Error rc': 0, 'Time': 0.02317023277282715}], 'Solution': [OrderedDict([('number of solutions', 0), ('number of solutions displayed', 0)])]}"
- ]
- },
- "execution_count": 36,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "solver.solve(m, tee=True)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "IPOPT should have returned optimal solution now, so it looks like those bounds were what was causing the model to be infeasible. At this point, the model is now solving (for the current values at least), so you might think that the model is now ready for optimization.\n",
- "\n",
- "However, if we look at the solver logs we can see that it took around 3 iterations for IPOPT to solve our model (depending on minor variations in computer architecture). For a model this simple, we would generally expect it to solve in only 1 iteration so there is still some room for improvement.\n",
- "\n",
- "\n",
- "Warning:\n",
- "You should keep in mind that just because you get an optimal solution does not mean that your model is robust and free of issues.\n",
- " \n",
- "You should always take the time to look over the solver logs to look for signs of trouble, even if you get an optimal solution. While you might get an optimal solution for the current state, there may be advance warning signs of issues that will cause problems later when you try to solve the model at a different state.\n",
- "
\n",
- "\n",
- "Let us run the ``report_numerical_issues`` method again to see if there are any other problems we need to address.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Check for additional numerical issues in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 37,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Check for additional numerical issues"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 38,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Model Statistics\n",
- "\n",
- " Jacobian Condition Number: 1.700E+01\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "0 WARNINGS\n",
- "\n",
- " No warnings found!\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "5 Cautions\n",
- "\n",
- " Caution: 1 Variable with value close to their bounds (abs=1.0E-04, rel=1.0E-04)\n",
- " Caution: 1 Variable with value close to zero (tol=1.0E-08)\n",
- " Caution: 1 Variable with extreme value (<1.0E-04 or >1.0E+04)\n",
- " Caution: 1 Variable with None value\n",
- " Caution: 1 extreme Jacobian Entry (<1.0E-04 or >1.0E+04)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "Suggested next steps:\n",
- "\n",
- " If you still have issues converging your model consider:\n",
- " prepare_svd_toolbox()\n",
- " prepare_degeneracy_hunter()\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.report_numerical_issues()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The toolbox is not reporting any warnings which is good, however there are still 5 numerical cautions that it has identified which might be contributing to the larger than expected number of iterations. As mentioned earlier, the toolbox does not suggest methods for investigating these, but there are methods available. For example, we can look at the variable with an extreme value using the `display_variables_with_extreme_values()` method.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Check for additional information about variables with extreme values.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 39,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Display variable with extreme value"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 40,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "The following variable(s) have extreme values (<1.0E-04 or > 1.0E+04):\n",
- "\n",
- " v7: 4.9999999999999945e-08\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.display_variables_with_extreme_values()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We can see that ``v7`` is potentially causing problems due to having a very small value (on the order of magnitude of the solver tolerance). This can be especially problematic for interior point solvers like IPOPT if there is a lower bound of 0 (which there is in this case). IPOPT tries to avoid bounds and thus perturbs solutions away from these if it gets too close, which can cause convergence to be slow (or fail) if the solution lies close to the bound.\n",
- "\n",
- "We can address this by scaling the variable so that the value of the scaled variable is large enough that the solution is not close to the lower bound. Additionally, we should look at any constraint that ``v7`` appears in (in this case ``c4``) and ensure that those constraints are well scaled as well (so that a residual of 1e-6 is reasonable for the terms involved).\n",
- "\n",
- "For this case, we can set a scaling factor of 1e8 for both ``v7`` and ``c4`` as shown below. Note that we also need to apply Pyomo's scaling transformation to create a new scaled model to work with."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 41,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.scaling_factor = pyo.Suffix(direction=pyo.Suffix.EXPORT)\n",
- "\n",
- "m.scaling_factor[m.v7] = 1e8\n",
- "m.scaling_factor[m.c4] = 1e8\n",
- "\n",
- "scaling = pyo.TransformationFactory('core.scale_model')\n",
- "scaled_model = scaling.create_using(m, rename=False)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Now that we have a scaled model, we can try to solve it and hopefully see better convergence than the unscaled model.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Solve the scaled model and check to see how many iterations are required.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 42,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Solve scaled model"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 43,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "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...: 7\n",
- "Number of nonzeros in inequality constraint Jacobian.: 0\n",
- "Number of nonzeros in Lagrangian Hessian.............: 0\n",
- "\n",
- "Total number of variables............................: 4\n",
- " variables with only lower bounds: 0\n",
- " variables with lower and upper bounds: 2\n",
- " variables with only upper bounds: 0\n",
- "Total number of equality constraints.................: 4\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 5.33e-15 0.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
- "\n",
- "Number of Iterations....: 0\n",
- "\n",
- " (scaled) (unscaled)\n",
- "Objective...............: 0.0000000000000000e+00 0.0000000000000000e+00\n",
- "Dual infeasibility......: 0.0000000000000000e+00 0.0000000000000000e+00\n",
- "Constraint violation....: 5.3290705182007514e-15 5.3290705182007514e-15\n",
- "Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
- "Overall NLP error.......: 5.3290705182007514e-15 5.3290705182007514e-15\n",
- "\n",
- "\n",
- "Number of objective function evaluations = 1\n",
- "Number of objective gradient evaluations = 1\n",
- "Number of equality constraint evaluations = 1\n",
- "Number of inequality constraint evaluations = 0\n",
- "Number of equality constraint Jacobian evaluations = 1\n",
- "Number of inequality constraint Jacobian evaluations = 0\n",
- "Number of Lagrangian Hessian evaluations = 0\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"
- ]
- },
- {
- "data": {
- "text/plain": [
- "{'Problem': [{'Lower bound': -inf, 'Upper bound': inf, 'Number of objectives': 1, 'Number of constraints': 4, 'Number of variables': 4, 'Sense': 'unknown'}], 'Solver': [{'Status': 'ok', 'Message': 'Ipopt 3.13.2\\\\x3a Optimal Solution Found', 'Termination condition': 'optimal', 'Id': 0, 'Error rc': 0, 'Time': 0.0058002471923828125}], 'Solution': [OrderedDict([('number of solutions', 0), ('number of solutions displayed', 0)])]}"
- ]
- },
- "execution_count": 43,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "solver.solve(scaled_model, tee=True)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "As we can see, the scaled model solved in 0 iterations (indicating that it already had the right solution). However, had we done this to the unscaled model we would have found it required 2-3 iterations again due to IPOPT perturbing the initial (correct) solution away from the bounds.\n",
- "\n",
- "\n",
- "Warning:\n",
- "Normally in these cases we would need to map the solution from the scaled model back to the unscaled model so we can view the results. In this case, we are not actually interested in the solution so we move on with the model diagnosis.\n",
- "
\n",
- "\n",
- "Now that we have fixed the scaling issues, we can go back to the ``DiagnosticsToolbox`` and see if we still have any warnings. Note however that we need to look at the scaled model now rather than the original model, so we need to create a new instance of the ``DiagnositcsToolbox`` with the scaled model as the ``model`` argument.\n",
- "\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Create a new instance of the DiagnosticsToolbox and check the scaled model for issues.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 45,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Create a new diagnostics toolbox for scaled model\n",
- "\n",
- "# Report numerical issues for scaled model\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 46,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Model Statistics\n",
- "\n",
- " Jacobian Condition Number: 1.800E+01\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "0 WARNINGS\n",
- "\n",
- " No warnings found!\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "3 Cautions\n",
- "\n",
- " Caution: 1 Variable with value close to their bounds (abs=1.0E-04, rel=1.0E-04)\n",
- " Caution: 1 Variable with value close to zero (tol=1.0E-08)\n",
- " Caution: 1 Variable with None value\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "Suggested next steps:\n",
- "\n",
- " If you still have issues converging your model consider:\n",
- " prepare_svd_toolbox()\n",
- " prepare_degeneracy_hunter()\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt_scaled = DiagnosticsToolbox(scaled_model)\n",
- "dt_scaled.report_numerical_issues()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We can see that applying scaling addressed two of the cautions we had before (the variable with an extreme value and an associated large value in the model Jacobian). Whilst we were able to solve the unscaled model in this case, this is in part because it was a simple linear model. In more complex, non-linear models, scaling becomes much more important and often depends strongly on the current state of the model. That is, you can often find cases where the unscaled (or poorly scaled) model solves for a limited range of conditions but fails to solve if you move too far away for the current state. Whilst you might be able to solve the model at the current state, you should always check the solver logs and numerical cautions for advanced warning signs of scaling issues that might manifest later when you try to solve the model for a different state (e.g., during optimization).\n",
- "\n",
- "\n",
- "Warning:\n",
- "By their nature, numerical issues depend on the current values of the variables in the model, and thus may remain hidden until someone tries to solve the model close to where the issue exists. For this reason, the full model diagnostics workflow contains steps to run the numerical checks across a wide range of variable values to try to ensure that no issues remain hidden. This is beyond the scope of this tutorial however.\n",
- "
\n",
- "\n",
- "At this point, we have addressed all the issues that were preventing us from solving the demonstration model and so reached the end of this tutorial. For cases where we are still having trouble solving the model, we can see that the toolbox is suggesting additional methods for further debugging and these advanced features will be the focus of separate tutorials."
- ]
+ "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": [
+ "# IDAES Model Diagnostics Toolbox Tutorial\n",
+ "Author: Andrew Lee \n",
+ "Maintainer: Andrew Lee \n",
+ "Updated: 2023-10-31 \n",
+ "\n",
+ "As you have likely discovered already, developing and solving models in an equation-oriented (EO) environment can be challenging and often takes a significant amount of effort. There are many pitfalls and mistakes that can be encountered when developing a model which can greatly impact the solvability and robustness of the final problem.\n",
+ "\n",
+ "Model diagnosis and debugging is often more of an art than a science, and it generally relies on significant experience and understanding both of general EO modeling techniques and the specific model and problem being solved. To assist with this process, IDAES has developed a model diagnostics toolbox that brings together a large number of tools for identifying potential issues in a model to help guide the user through the process of finding and resolving these issues. Note however that whilst these tools can help identify the presence of an issue, remedying the issue always requires some degree of engineering knowledge about the system being modeled, and thus it is ultimately up to the user to find a solution to the problem.\n",
+ "\n",
+ "This tutorial will take you through using the {py:class}`DiagnosticsToolbox ` to debug a number of issues in a simple Pyomo model and to take it from initially reporting a possible infeasible solution to returning the correct solution.\n",
+ "\n",
+ "To get started, the ``DiagnosticsToolbox`` can be imported from ``idaes.core.util``.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Import the DiagnosticsToolbox in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from idaes.core.util import DiagnosticsToolbox"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "To get some information on where to start, try using the Python ``help()`` function to see the documentation for the ``DiagnosticsToolbox``.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Call `help(DiagnosticsToolbox)` to see some more information on the toolbox and some instructions on how to get started.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Call the help() function for more information"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Help on class DiagnosticsToolbox in module idaes.core.util.model_diagnostics:\n",
+ "\n",
+ "class DiagnosticsToolbox(builtins.object)\n",
+ " | DiagnosticsToolbox(model: pyomo.core.base.block._BlockData, **kwargs)\n",
+ " | \n",
+ " | The IDAES Model DiagnosticsToolbox.\n",
+ " | \n",
+ " | To get started:\n",
+ " | \n",
+ " | 1. Create an instance of your model (this does not need to be initialized yet).\n",
+ " | 2. Fix variables until you have 0 degrees of freedom. Many of these tools presume\n",
+ " | a square model, and a square model should always be the foundation of any more\n",
+ " | advanced model.\n",
+ " | 3. Create an instance of the DiagnosticsToolbox and provide the model to debug as\n",
+ " | the model argument.\n",
+ " | 4. Call the ``report_structural_issues()`` method.\n",
+ " | \n",
+ " | Model diagnostics is an iterative process and you will likely need to run these\n",
+ " | tools multiple times to resolve all issues. After making a change to your model,\n",
+ " | you should always start from the beginning again to ensure the change did not\n",
+ " | introduce any new issues; i.e., always start from the report_structural_issues()\n",
+ " | method.\n",
+ " | \n",
+ " | Note that structural checks do not require the model to be initialized, thus users\n",
+ " | should start with these. Numerical checks require at least a partial solution to the\n",
+ " | model and should only be run once all structural issues have been resolved.\n",
+ " | \n",
+ " | Report methods will print a summary containing three parts:\n",
+ " | \n",
+ " | 1. Warnings - these are critical issues that should be resolved before continuing.\n",
+ " | For each warning, a method will be suggested in the Next Steps section to get\n",
+ " | additional information.\n",
+ " | 2. Cautions - these are things that could be correct but could also be the source of\n",
+ " | solver issues. Not all cautions need to be addressed, but users should investigate\n",
+ " | each one to ensure that the behavior is correct and that they will not be the source\n",
+ " | of difficulties later. Methods exist to provide more information on all cautions,\n",
+ " | but these will not appear in the Next Steps section.\n",
+ " | 3. Next Steps - these are recommended methods to call from the DiagnosticsToolbox to\n",
+ " | get further information on warnings. If no warnings are found, this will suggest\n",
+ " | the next report method to call.\n",
+ " | \n",
+ " | Args:\n",
+ " | \n",
+ " | model: model to be diagnosed. The DiagnosticsToolbox does not support indexed Blocks.\n",
+ " | \n",
+ " | Keyword Arguments\n",
+ " | -----------------\n",
+ " | variable_bounds_absolute_tolerance: float, default=0.0001\n",
+ " | Absolute tolerance for considering a variable to be close to its\n",
+ " | bounds.\n",
+ " | \n",
+ " | variable_bounds_relative_tolerance: float, default=0.0001\n",
+ " | Relative tolerance for considering a variable to be close to its\n",
+ " | bounds.\n",
+ " | \n",
+ " | variable_bounds_violation_tolerance: float, default=0\n",
+ " | Absolute tolerance for considering a variable to violate its bounds.\n",
+ " | Some solvers relax bounds on variables thus allowing a small violation\n",
+ " | to be considered acceptable.\n",
+ " | \n",
+ " | constraint_residual_tolerance: float, default=1e-05\n",
+ " | Absolute tolerance to use when checking constraint residuals.\n",
+ " | \n",
+ " | variable_large_value_tolerance: float, default=10000.0\n",
+ " | Absolute tolerance for considering a value to be large.\n",
+ " | \n",
+ " | variable_small_value_tolerance: float, default=0.0001\n",
+ " | Absolute tolerance for considering a value to be small.\n",
+ " | \n",
+ " | variable_zero_value_tolerance: float, default=1e-08\n",
+ " | Absolute tolerance for considering a value to be near to zero.\n",
+ " | \n",
+ " | jacobian_large_value_caution: float, default=10000.0\n",
+ " | Tolerance for raising a caution for large Jacobian values.\n",
+ " | \n",
+ " | jacobian_large_value_warning: float, default=100000000.0\n",
+ " | Tolerance for raising a warning for large Jacobian values.\n",
+ " | \n",
+ " | jacobian_small_value_caution: float, default=0.0001\n",
+ " | Tolerance for raising a caution for small Jacobian values.\n",
+ " | \n",
+ " | jacobian_small_value_warning: float, default=1e-08\n",
+ " | Tolerance for raising a warning for small Jacobian values.\n",
+ " | \n",
+ " | warn_for_evaluation_error_at_bounds: bool, default=True\n",
+ " | If False, warnings will not be generated for things like log(x) with x\n",
+ " | >= 0\n",
+ " | \n",
+ " | Methods defined here:\n",
+ " | \n",
+ " | __init__(self, model: pyomo.core.base.block._BlockData, **kwargs)\n",
+ " | Initialize self. See help(type(self)) for accurate signature.\n",
+ " | \n",
+ " | assert_no_numerical_warnings(self)\n",
+ " | Checks for numerical warnings in the model and raises an AssertionError\n",
+ " | if any are found.\n",
+ " | \n",
+ " | Raises:\n",
+ " | AssertionError if any warnings are identified by numerical analysis.\n",
+ " | \n",
+ " | assert_no_structural_warnings(self)\n",
+ " | Checks for structural warnings in the model and raises an AssertionError\n",
+ " | if any are found.\n",
+ " | \n",
+ " | Raises:\n",
+ " | AssertionError if any warnings are identified by structural analysis.\n",
+ " | \n",
+ " | display_components_with_inconsistent_units(self, stream=None)\n",
+ " | Prints a list of all Constraints, Expressions and Objectives in the\n",
+ " | model with inconsistent units of measurement.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_constraints_with_extreme_jacobians(self, stream=None)\n",
+ " | Prints the constraints associated with rows in the Jacobian with extreme\n",
+ " | L2 norms. This often indicates poorly scaled constraints.\n",
+ " | \n",
+ " | Tolerances can be set via the DiagnosticsToolbox config.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the output to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_constraints_with_large_residuals(self, stream=None)\n",
+ " | Prints a list of Constraints with residuals greater than a specified tolerance.\n",
+ " | Tolerance can be set in the class configuration options.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_external_variables(self, stream=None)\n",
+ " | Prints a list of variables that appear within activated Constraints in the\n",
+ " | model but are not contained within the model themselves.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_extreme_jacobian_entries(self, stream=None)\n",
+ " | Prints variables and constraints associated with entries in the Jacobian with extreme\n",
+ " | values. This can be indicative of poor scaling, especially for isolated terms (e.g.\n",
+ " | variables which appear only in one term of a single constraint).\n",
+ " | \n",
+ " | Tolerances can be set via the DiagnosticsToolbox config.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the output to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_overconstrained_set(self, stream=None)\n",
+ " | Prints the variables and constraints in the over-constrained sub-problem\n",
+ " | from a Dulmage-Mendelsohn partitioning.\n",
+ " | \n",
+ " | This can be used to identify the over-defined part of a model and thus\n",
+ " | where constraints must be removed or variables unfixed.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_potential_evaluation_errors(self, stream=None)\n",
+ " | Prints constraints that may be prone to evaluation errors\n",
+ " | (e.g., log of a negative number) based on variable bounds.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the output to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_underconstrained_set(self, stream=None)\n",
+ " | Prints the variables and constraints in the under-constrained sub-problem\n",
+ " | from a Dulmage-Mendelsohn partitioning.\n",
+ " | \n",
+ " | This can be used to identify the under-defined part of a model and thus\n",
+ " | where additional information (fixed variables or constraints) are required.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_unused_variables(self, stream=None)\n",
+ " | Prints a list of variables that do not appear in any activated Constraints.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_variables_at_or_outside_bounds(self, stream=None)\n",
+ " | Prints a list of variables with values that fall at or outside the bounds\n",
+ " | on the variable.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_variables_fixed_to_zero(self, stream=None)\n",
+ " | Prints a list of variables that are fixed to an absolute value of 0.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_variables_near_bounds(self, stream=None)\n",
+ " | Prints a list of variables with values close to their bounds. Tolerance can\n",
+ " | be set in the class configuration options.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_variables_with_extreme_jacobians(self, stream=None)\n",
+ " | Prints the variables associated with columns in the Jacobian with extreme\n",
+ " | L2 norms. This often indicates poorly scaled variables.\n",
+ " | \n",
+ " | Tolerances can be set via the DiagnosticsToolbox config.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the output to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_variables_with_extreme_values(self, stream=None)\n",
+ " | Prints a list of variables with extreme values.\n",
+ " | \n",
+ " | Tolerances can be set in the class configuration options.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_variables_with_none_value(self, stream=None)\n",
+ " | Prints a list of variables with a value of None.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_variables_with_value_near_zero(self, stream=None)\n",
+ " | Prints a list of variables with a value close to zero. The tolerance\n",
+ " | for determining what is close to zero can be set in the class configuration\n",
+ " | options.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | get_dulmage_mendelsohn_partition(self)\n",
+ " | Performs a Dulmage-Mendelsohn partitioning on the model and returns\n",
+ " | the over- and under-constrained sub-problems.\n",
+ " | \n",
+ " | Returns:\n",
+ " | list-of-lists variables in each independent block of the under-constrained set\n",
+ " | list-of-lists constraints in each independent block of the under-constrained set\n",
+ " | list-of-lists variables in each independent block of the over-constrained set\n",
+ " | list-of-lists constraints in each independent block of the over-constrained set\n",
+ " | \n",
+ " | prepare_degeneracy_hunter(self, **kwargs)\n",
+ " | Create an instance of the DegeneracyHunter and store as self.degeneracy_hunter.\n",
+ " | \n",
+ " | After creating an instance of the toolbox, call\n",
+ " | report_irreducible_degenerate_sets.\n",
+ " | \n",
+ " | Returns:\n",
+ " | \n",
+ " | Instance of DegeneracyHunter\n",
+ " | \n",
+ " | Keyword Arguments\n",
+ " | -----------------\n",
+ " | solver: str, default='scip'\n",
+ " | MILP solver to use for finding irreducible degenerate sets.\n",
+ " | \n",
+ " | solver_options: optional\n",
+ " | Options to pass to MILP solver.\n",
+ " | \n",
+ " | M: float, default=100000.0\n",
+ " | Maximum value for nu in MILP models.\n",
+ " | \n",
+ " | m_small: float, default=1e-05\n",
+ " | Smallest value for nu to be considered non-zero in MILP models.\n",
+ " | \n",
+ " | trivial_constraint_tolerance: float, default=1e-06\n",
+ " | Tolerance for identifying non-zero rows in Jacobian.\n",
+ " | \n",
+ " | prepare_svd_toolbox(self, **kwargs)\n",
+ " | Create an instance of the SVDToolbox and store as self.svd_toolbox.\n",
+ " | \n",
+ " | After creating an instance of the toolbox, call\n",
+ " | display_underdetermined_variables_and_constraints().\n",
+ " | \n",
+ " | Returns:\n",
+ " | \n",
+ " | Instance of SVDToolbox\n",
+ " | \n",
+ " | Keyword Arguments\n",
+ " | -----------------\n",
+ " | number_of_smallest_singular_values: PositiveInt, optional\n",
+ " | Number of smallest singular values to compute\n",
+ " | \n",
+ " | svd_callback: svd_callback_validator, default=\n",
+ " | Callback to SVD method of choice (default = svd_dense). Callbacks\n",
+ " | should take the Jacobian and number of singular values to compute as\n",
+ " | options, plus any method specific arguments, and should return the u,\n",
+ " | s and v matrices as numpy arrays.\n",
+ " | \n",
+ " | svd_callback_arguments: dict, optional\n",
+ " | Optional arguments to pass to SVD callback (default = None)\n",
+ " | \n",
+ " | singular_value_tolerance: float, default=1e-06\n",
+ " | Tolerance for defining a small singular value\n",
+ " | \n",
+ " | size_cutoff_in_singular_vector: float, default=0.1\n",
+ " | Size below which to ignore constraints and variables in the singular\n",
+ " | vector\n",
+ " | \n",
+ " | report_numerical_issues(self, stream=None)\n",
+ " | Generates a summary report of any numerical issues identified in the model provided\n",
+ " | and suggest next steps for debugging model.\n",
+ " | \n",
+ " | Numerical checks should only be performed once all structural issues have been resolved,\n",
+ " | and require that at least a partial solution to the model is available.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: I/O object to write report to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | report_structural_issues(self, stream=None)\n",
+ " | Generates a summary report of any structural issues identified in the model provided\n",
+ " | and suggests next steps for debugging the model.\n",
+ " | \n",
+ " | This should be the first method called when debugging a model and after any change\n",
+ " | is made to the model. These checks can be run before trying to initialize and solve\n",
+ " | the model.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: I/O object to write report to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | ----------------------------------------------------------------------\n",
+ " | Readonly properties defined here:\n",
+ " | \n",
+ " | model\n",
+ " | Model currently being diagnosed.\n",
+ " | \n",
+ " | ----------------------------------------------------------------------\n",
+ " | Data descriptors defined here:\n",
+ " | \n",
+ " | __dict__\n",
+ " | dictionary for instance variables (if defined)\n",
+ " | \n",
+ " | __weakref__\n",
+ " | list of weak references to the object (if defined)\n",
+ "\n"
+ ]
}
- ],
- "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"
+ ],
+ "source": [
+ "help(DiagnosticsToolbox)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The ``help()`` function gives us a lot of information on the ``DiagnosticsToolbox`` and all the methods that it supports (and there are many). However, the important part to start with are the four steps outlined at the top of the doc string that tell us how to get started.\n",
+ "\n",
+ "Firstly, we need a model to test (and, for this tutorial at least, one that has a wide range of issues that we need to fix before it will solve). We then also need to fix some variables so that we have 0 degrees of freedom in our model. Whilst our ultimate goal is generally optimization (and thus a system with 1 or more degrees of freedom), all models conceptually derive from a square model representing a nominal state. If this nominal state is not well-posed, then any issues present will also be present in the resulting optimization (even if adding degrees of freedom means that the model is now easier to solve).\n",
+ "\n",
+ "The cell below contains a demonstration model for this tutorial that contains a number of issues that we will resolve using the ``DiagnosticsToolbox``."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import pyomo.environ as pyo\n",
+ "\n",
+ "m = pyo.ConcreteModel()\n",
+ "\n",
+ "m.v1 = pyo.Var(units=pyo.units.m)\n",
+ "m.v2 = pyo.Var(units=pyo.units.m)\n",
+ "m.v3 = pyo.Var(bounds=(0, 5))\n",
+ "m.v4 = pyo.Var()\n",
+ "m.v5 = pyo.Var(bounds=(0, 10))\n",
+ "m.v6 = pyo.Var()\n",
+ "m.v7 = pyo.Var(\n",
+ " units=pyo.units.m, bounds=(0, 1)\n",
+ ") # Poorly scaled variable with lower bound\n",
+ "m.v8 = pyo.Var() # unused variable\n",
+ "\n",
+ "m.c1 = pyo.Constraint(expr=m.v1 + m.v2 == 10) # Unit consistency issue\n",
+ "m.c2 = pyo.Constraint(expr=m.v3 == m.v4 + m.v5)\n",
+ "m.c3 = pyo.Constraint(expr=2 * m.v3 == 3 * m.v4 + 4 * m.v5 + m.v6)\n",
+ "m.c4 = pyo.Constraint(expr=m.v7 == 1e-8 * m.v1) # Poorly scaled constraint\n",
+ "\n",
+ "m.v4.fix(2)\n",
+ "m.v5.fix(2)\n",
+ "m.v6.fix(0)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Next, the instructions tell us to create an instance of the ``DiagnosticsToolbox`` and to pass the model we wish to examine as an argument.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Create an instance of the DiagnosticsToolbox: dt = DiagnosticsToolbox(m)\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Create an instance of the Diagnostics Toolbox"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "dt = DiagnosticsToolbox(m)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Finally, the instructions tell us to run the ``report_structural_issues()`` method. Structural issues represent issues that exist solely in the form of the model equations and thus do not depend on the current value of any of the variables. This is useful as it means we can check for these before we even call a solver, which can be critical as sometimes these issues will cause a solver to fail without providing a useful solution.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Call dt.report_structural_issues() in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Call the report_structural_issues() method"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Activated Blocks: 1 (Deactivated: 0)\n",
+ " Free Variables in Activated Constraints: 4 (External: 0)\n",
+ " Free Variables with only lower bounds: 0\n",
+ " Free Variables with only upper bounds: 0\n",
+ " Free Variables with upper and lower bounds: 2\n",
+ " Fixed Variables in Activated Constraints: 3 (External: 0)\n",
+ " Activated Equality Constraints: 4 (Deactivated: 0)\n",
+ " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
+ " Activated Objectives: 0 (Deactivated: 0)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 WARNINGS\n",
+ "\n",
+ " WARNING: 1 Component with inconsistent units\n",
+ " WARNING: Structural singularity found\n",
+ " Under-Constrained Set: 3 variables, 2 constraints\n",
+ " Over-Constrained Set: 1 variables, 2 constraints\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 Cautions\n",
+ "\n",
+ " Caution: 1 variable fixed to 0\n",
+ " Caution: 1 unused variable (0 fixed)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " display_components_with_inconsistent_units()\n",
+ " display_underconstrained_set()\n",
+ " display_overconstrained_set()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Looking at the output from the ``report_structural_issues()`` method, we can see that it provides a fairly short summary containing 4 sections.\n",
+ "\n",
+ "1. The first section is a summary of the size of the model, indicating things like the number of variables and constraints. The size of the model is often important for judging how difficult it will be to solve, and this information can also be useful for comparison to what is being sent to the solver. Most solvers will report the size of the model in their output logs, and if there is a difference between what is reported here and by the solver, then you should probably look into what is happening. This section also notes some things such as if you have any deactivated Blocks, Constraints or Objectives, or if you have variables which appear in the constraints that are not part of the model; these are not necessarily wrong but it is easy to have accidentally deactivated something you did not intend to so you should always check to see that these are expected.\n",
+ "\n",
+ "2. The second section provides a summary of any critical structural issues that were found - in this case we can see that there are 2 warnings we are going to need to look into. Warnings represent issues that need to be addressed before moving on as these will likely cause the solver to fail or give an incorrect answer.\n",
+ "\n",
+ "3. The third section lists a summary of any cautions that are found. Cautions represent issues that may or may not be problematic; in many cases these might be expected behaviors or borderline issues. However, these could also represent conceptual issues that should be addressed, so users should take the time to investigate these and determine if they need to be fixed or not.\n",
+ "\n",
+ "4. Finally, there is a section that suggests the next steps to take to help guide you through the model diagnosis process. If any warnings were identified, this section will list methods that can help you get more information on each specific problem, and if no warnings are found then it will guide you onto the next step in the model diagnosis workflow.\n",
+ "\n",
+ "**Note:** there are methods available to help investigate cautions as well, but these will not show up in the next steps in order to avoid cluttering the output. You can get more information on the available methods for investigating cautions via the documentation or ``help()`` function.\n",
+ "\n",
+ "In our current model, we have 2 critical issues (warnings) that we need to look into and resolve. The order in which we resolve these will generally not matter, but be aware that these can often be interrelated - fixing one warning might resolve other warnings as well (or create new ones), and sometimes you will need to look at multiple issues together to find the overall root cause.\n",
+ "\n",
+ "To start with, let us look at the unit consistency issue. From the \"Next Steps\" section above, the toolbox is suggesting we run the ``display_components_with_inconsistent_units()`` method for more information.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Call the `display_components_with_inconsistent_units()` method from the DiagnosticsToolbox to see more information on which constraint is causing the unit consistency issues.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Call the display_components_with_inconsistent_units() method"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {
+ "scrolled": false,
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "The following component(s) have unit consistency issues:\n",
+ "\n",
+ " c1\n",
+ "\n",
+ "For more details on unit inconsistencies, import the assert_units_consistent method\n",
+ "from pyomo.util.check_units\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.display_components_with_inconsistent_units()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This tells us that the issue lies in constraint ``c1``. If we go back and look at this constraint, we can see that it says ``v1 + v2 == 10``. ``v1`` and ``v2`` both have units of ``m`` which is consistent, but the constant in the expression (right hand side) is unitless. Thus, we need to correct this so that the right hand side has units for the constraint to be consistent.\n",
+ "\n",
+ "The cell below shows how to delete a constraint and replace it with a new one with the correct units.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "Deleting components can cause unexpected issues if something else in a model is using that component (e.g., deleting a variable which is used in a constraint). You should always be careful when deleting Pyomo components and make sure you only delete components that are not used elsewhere.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Delete the incorrect Constraint\n",
+ "m.del_component(m.c1)\n",
+ "\n",
+ "# Re-create the Constraint with the correct units\n",
+ "m.c1 = pyo.Constraint(expr=m.v1 + m.v2 == 10 * pyo.units.m)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Warning:\n",
+ "Fixing issues in models is often an iterative process requiring trial and error. You might also have some results from a model before running the diagnostics tools and the changes you make during debugging may make it difficult to replicate those results afterwards.\n",
+ " \n",
+ "It is strongly recommended that you keep a record of the changes you make at each step and why, along with a Git hash (or similar version control marker) corresponding to these changes. This will allow you see what changes and why, and give you a way to go back to previous iterations if the current approach does not work out. The IDAES documentation contains recommendations on how to keep and maintain a modeling logbook.\n",
+ "
\n",
+ "\n",
+ "Now, re-run the ``report_structural_issues()`` method and see if this change has fixed the unit consistency issue.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Call dt.report_structural_issues() in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Call the report_structural_issues() method"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {
+ "scrolled": true,
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Activated Blocks: 1 (Deactivated: 0)\n",
+ " Free Variables in Activated Constraints: 4 (External: 0)\n",
+ " Free Variables with only lower bounds: 0\n",
+ " Free Variables with only upper bounds: 0\n",
+ " Free Variables with upper and lower bounds: 2\n",
+ " Fixed Variables in Activated Constraints: 3 (External: 0)\n",
+ " Activated Equality Constraints: 4 (Deactivated: 0)\n",
+ " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
+ " Activated Objectives: 0 (Deactivated: 0)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "1 WARNINGS\n",
+ "\n",
+ " WARNING: Structural singularity found\n",
+ " Under-Constrained Set: 3 variables, 2 constraints\n",
+ " Over-Constrained Set: 1 variables, 2 constraints\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 Cautions\n",
+ "\n",
+ " Caution: 1 variable fixed to 0\n",
+ " Caution: 1 unused variable (0 fixed)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " display_underconstrained_set()\n",
+ " display_overconstrained_set()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The unit consistency issue has been resolved by the changes above, so now we need to look at the structural singularity. A structural singularity occurs when one sub-part of the model is over-constrained (negative degrees of freedom), which generally means another part is under-constrained (positive degrees of freedom, assuming that there are 0 degrees of freedom overall).\n",
+ "\n",
+ "The toolbox is suggesting we use the ``display_overconstrained_set()`` and ``display_underconstrained_set()`` methods to get more information on the singularity; for now, let us start with the over-constrained set.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Call dt.display_overconstrained_set() in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Call the display_overconstrained_set() method"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Dulmage-Mendelsohn Over-Constrained Set\n",
+ "\n",
+ " Independent Block 0:\n",
+ "\n",
+ " Variables:\n",
+ "\n",
+ " v3\n",
+ "\n",
+ " Constraints:\n",
+ "\n",
+ " c2\n",
+ " c3\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.display_overconstrained_set()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "From the output above, the toolbox is telling us that we have two constraints (``c2`` and ``c3``) which only contain a single unfixed variable (``v3``); thus in this part of the model we have -1 degree of freedom and the model is not well defined (structurally singular). If we go back and look at these constraints, we can see the that the constraints are:\n",
+ "\n",
+ "``c2: v3 == v4 + v5``\n",
+ "\n",
+ "``c3: 2*v3 == 3*v4 + 4*v5 + v6``\n",
+ "\n",
+ "We can see that in addition to ``v3`` these constraints actually contain 3 other variables (``v4``, ``v5`` and ``v6``), however these are all variables we fixed to get our initial zero degrees of freedom. It looks like we have either accidentally fixed one too many variables or written one too many constraints.\n",
+ "\n",
+ "For this example, let us assume that ``v4`` was not supposed to be fixed and unfix it.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Resolve the structural singularity and then call dt.report_structural_issues() in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Unfix v4\n",
+ "\n",
+ "# Then call the report_structural_issues() method again"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "metadata": {
+ "scrolled": true,
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Activated Blocks: 1 (Deactivated: 0)\n",
+ " Free Variables in Activated Constraints: 5 (External: 0)\n",
+ " Free Variables with only lower bounds: 0\n",
+ " Free Variables with only upper bounds: 0\n",
+ " Free Variables with upper and lower bounds: 2\n",
+ " Fixed Variables in Activated Constraints: 2 (External: 0)\n",
+ " Activated Equality Constraints: 4 (Deactivated: 0)\n",
+ " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
+ " Activated Objectives: 0 (Deactivated: 0)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 WARNINGS\n",
+ "\n",
+ " WARNING: 1 Degree of Freedom\n",
+ " WARNING: Structural singularity found\n",
+ " Under-Constrained Set: 3 variables, 2 constraints\n",
+ " Over-Constrained Set: 0 variables, 0 constraints\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 Cautions\n",
+ "\n",
+ " Caution: 1 variable fixed to 0\n",
+ " Caution: 1 unused variable (0 fixed)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " display_underconstrained_set()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
}
+ ],
+ "source": [
+ "m.v4.unfix()\n",
+ "\n",
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We can see that the over-constrained set is now empty (0 variables and 0 constraints) but the under-constrained set still has 3 variables and only 2 constraints. We can also see that there is a new warning about having 1 degree of freedom in the model, however this should not be surprising as we have just unfixed ``v4`` to resolve the over-constrained set so we have added a degree of freedom to the model.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Display the under-constrained set in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Display the under-constrained set"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 20,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Dulmage-Mendelsohn Under-Constrained Set\n",
+ "\n",
+ " Independent Block 0:\n",
+ "\n",
+ " Variables:\n",
+ "\n",
+ " v2\n",
+ " v1\n",
+ " v7\n",
+ "\n",
+ " Constraints:\n",
+ "\n",
+ " c1\n",
+ " c4\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.display_underconstrained_set()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Looking at the output from the ``display_underconstrained_set()`` method, we can see that we have two constraints, ``c1`` and ``c4``, which contain three unfixed variables, ``v1``, ``v2`` and ``v7``. Thus, we have one degree of freedom that needs to be addressed. To fix this, we could either fix one of the variables shown or add an additional equality constraint to the model.\n",
+ "\n",
+ "For this example let's fix ``v2`` to a value of 5 and then re-run the ``report_structural_issues()`` method.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Fix v2 to a value of 5 and then re-run dt.report_structural_issues.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Fix v2 = 5\n",
+ "\n",
+ "# Then re-run report_structural_issues() method"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "metadata": {
+ "scrolled": true,
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Activated Blocks: 1 (Deactivated: 0)\n",
+ " Free Variables in Activated Constraints: 4 (External: 0)\n",
+ " Free Variables with only lower bounds: 0\n",
+ " Free Variables with only upper bounds: 0\n",
+ " Free Variables with upper and lower bounds: 2\n",
+ " Fixed Variables in Activated Constraints: 3 (External: 0)\n",
+ " Activated Equality Constraints: 4 (Deactivated: 0)\n",
+ " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
+ " Activated Objectives: 0 (Deactivated: 0)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "0 WARNINGS\n",
+ "\n",
+ " No warnings found!\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 Cautions\n",
+ "\n",
+ " Caution: 1 variable fixed to 0\n",
+ " Caution: 1 unused variable (0 fixed)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " Try to initialize/solve your model and then call report_numerical_issues()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "m.v2.fix(5)\n",
+ "\n",
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The toolbox is now telling us that no warnings were found, so we have resolved all the structural issues (for now at least). The toolbox is telling us that there are also 2 non-critical issues (cautions) that we should look at; one about an unused variable and one about a variable fixed to zero. If you wish, you can look into identifying and fixing these yourself, however for this example we will move on to the next step (remember that the toolbox has methods to display more details for each of these which you can find in the documentation or from the ``help()`` function).\n",
+ "\n",
+ "For the Next Steps section, the toolbox is recommending we try to solve our model and then check for numerical issues.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Use the Pyomo SolverFactory to create an instance of IPOPT and then try to solve the model. Make sure to set \"tee=True\" as this is going to fail (and it is always good practice to review the solver logs).\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 23,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Create a solver object\n",
+ "\n",
+ "# Try to solve the model"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "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...: 7\n",
+ "Number of nonzeros in inequality constraint Jacobian.: 0\n",
+ "Number of nonzeros in Lagrangian Hessian.............: 0\n",
+ "\n",
+ "Total number of variables............................: 4\n",
+ " variables with only lower bounds: 0\n",
+ " variables with lower and upper bounds: 2\n",
+ " variables with only upper bounds: 0\n",
+ "Total number of equality constraints.................: 4\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 1.40e+01 0.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
+ " 1 0.0000000e+00 1.39e+01 1.50e+02 -1.0 6.00e+00 - 7.16e-01 4.93e-03h 1\n",
+ " 2 0.0000000e+00 1.39e+01 3.03e+06 -1.0 5.97e+00 - 1.00e+00 4.95e-05h 1\n",
+ " 3r 0.0000000e+00 1.39e+01 1.00e+03 1.1 0.00e+00 - 0.00e+00 2.47e-07R 2\n",
+ " 4r 0.0000000e+00 4.19e+00 9.42e+02 1.1 3.50e+03 - 4.02e-01 3.37e-03f 1\n",
+ " 5r 0.0000000e+00 2.12e+00 8.72e+02 1.1 5.89e+01 - 4.35e-01 7.06e-02f 1\n",
+ " 6r 0.0000000e+00 6.74e-01 6.06e+02 1.1 5.29e+00 - 9.93e-03 3.98e-01f 1\n",
+ " 7r 0.0000000e+00 6.80e-01 3.14e+02 0.4 2.05e-01 - 1.00e+00 1.03e-01f 1\n",
+ " 8r 0.0000000e+00 6.69e-01 2.78e-05 0.4 2.58e-02 - 1.00e+00 1.00e+00f 1\n",
+ " 9r 0.0000000e+00 6.67e-01 7.56e+00 -1.7 8.13e-03 - 9.93e-01 9.96e-01f 1\n",
+ "iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n",
+ " 10r 0.0000000e+00 6.67e-01 2.23e-07 -1.7 4.13e-05 - 1.00e+00 1.00e+00f 1\n",
+ " 11r 0.0000000e+00 6.67e-01 6.73e-01 -3.7 6.61e-05 - 1.00e+00 1.00e+00f 1\n",
+ " 12r 0.0000000e+00 6.67e-01 1.91e-09 -3.7 1.48e-09 - 1.00e+00 1.00e+00h 1\n",
+ " 13r 0.0000000e+00 6.67e-01 2.69e+00 -8.4 5.74e-07 - 1.00e+00 9.26e-01f 1\n",
+ " 14r 0.0000000e+00 6.67e-01 7.65e+01 -8.4 4.23e-08 - 8.68e-01 1.00e+00f 1\n",
+ "\n",
+ "Number of Iterations....: 14\n",
+ "\n",
+ " (scaled) (unscaled)\n",
+ "Objective...............: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Dual infeasibility......: 3.2644919411246030e-04 3.2644919411246030e-04\n",
+ "Constraint violation....: 6.6666666333656233e-01 6.6666666333656233e-01\n",
+ "Complementarity.........: 4.6615546565561981e-09 4.6615546565561981e-09\n",
+ "Overall NLP error.......: 6.6666666333656233e-01 6.6666666333656233e-01\n",
+ "\n",
+ "\n",
+ "Number of objective function evaluations = 18\n",
+ "Number of objective gradient evaluations = 5\n",
+ "Number of equality constraint evaluations = 18\n",
+ "Number of inequality constraint evaluations = 0\n",
+ "Number of equality constraint Jacobian evaluations = 17\n",
+ "Number of inequality constraint Jacobian evaluations = 0\n",
+ "Number of Lagrangian Hessian evaluations = 15\n",
+ "Total CPU secs in IPOPT (w/o function evaluations) = 0.003\n",
+ "Total CPU secs in NLP function evaluations = 0.000\n",
+ "\n",
+ "EXIT: Converged to a point of local infeasibility. Problem may be infeasible.\n",
+ "WARNING: Loading a SolverResults object with a warning status into\n",
+ "model.name=\"unknown\";\n",
+ " - termination condition: infeasible\n",
+ " - message from solver: Ipopt 3.13.2\\x3a Converged to a locally infeasible\n",
+ " point. Problem may be infeasible.\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "{'Problem': [{'Lower bound': -inf, 'Upper bound': inf, 'Number of objectives': 1, 'Number of constraints': 4, 'Number of variables': 4, 'Sense': 'unknown'}], 'Solver': [{'Status': 'warning', 'Message': 'Ipopt 3.13.2\\\\x3a Converged to a locally infeasible point. Problem may be infeasible.', 'Termination condition': 'infeasible', 'Id': 200, 'Error rc': 0, 'Time': 0.007064104080200195}], 'Solution': [OrderedDict([('number of solutions', 0), ('number of solutions displayed', 0)])]}"
+ ]
+ },
+ "execution_count": 24,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "solver = pyo.SolverFactory(\"ipopt\")\n",
+ "solver.solve(m, tee=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As hinted at above, IPOPT has returned a warning that the problem may be infeasible. Before moving on however, it is always good practice to look over the solver outputs and see what it is telling you.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "A lot of useful information is contained in the solver logs which is extremely useful when diagnosing modeling issues. Each solver has its own way of reporting output and its own specific behavior, so you will need to learn to interpret the output of each solver you use. The IDAES Documentation contains some guidance on interpreting output logs for a few common solvers.\n",
+ "
\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Call the report_numerical_issues method in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Check for numerical issues"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 26,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Jacobian Condition Number: 1.700E+01\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 WARNINGS\n",
+ "\n",
+ " WARNING: 1 Constraint with large residuals (>1.0E-05)\n",
+ " WARNING: 1 Variable at or outside bounds (tol=0.0E+00)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "5 Cautions\n",
+ "\n",
+ " Caution: 2 Variables with value close to their bounds (abs=1.0E-04, rel=1.0E-04)\n",
+ " Caution: 2 Variables with value close to zero (tol=1.0E-08)\n",
+ " Caution: 1 Variable with extreme value (<1.0E-04 or >1.0E+04)\n",
+ " Caution: 1 Variable with None value\n",
+ " Caution: 1 extreme Jacobian Entry (<1.0E-04 or >1.0E+04)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " display_constraints_with_large_residuals()\n",
+ " display_variables_at_or_outside_bounds()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.report_numerical_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The ``report_numerical_issues()`` provides a summary similar to that which we saw for the structural issues. Firstly, it reports to us the Jacobian condition number for our problem which can give us an idea of how well-scaled the problem is, followed by a list of warnings, cautions and suggested next steps.\n",
+ "\n",
+ "Unsurprisingly, we are seeing a warning about a constraint with a large residual which we would expect when a solver reports a potentially infeasible problem. We are also seeing a warning about a variable with bound violations which might be contributing to the potential infeasibility.\n",
+ "\n",
+ "For the next steps, the toolbox is suggesting some new methods to get more information on these issues; let us start by looking at the constraints with large residuals.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Display the constraint with a large residual in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 27,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Display constraint with large residual"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 28,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "The following constraint(s) have large residuals (>1.0E-05):\n",
+ "\n",
+ " c2: 6.66667E-01\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.display_constraints_with_large_residuals()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The toolbox is telling us that the constraint which failed to converge is ``c2``, however this is generally only part of the story. Solvers work by trying to minimize the infeasibility in the model (residual of the constraints), which generally means they push any infeasibility onto the least sensitive constraint in the problem. Thus, the constraint which shows the infeasibility is often not the root cause of the problem, but only the symptom of the underlying issue.\n",
+ "\n",
+ "If we look back at the constraints, we can see that the same variables also appear in ``c3`` and that some of these have bounds, all of which could be contributing to the infeasibility. In this case the solver tried to minimize the residual in all the constraints and ended up pushing all the issues off onto ``c2``.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "When dealing with solver issues such as this, you should always remember that the obvious symptoms are often just the tip of the iceberg and that the real issue generally lies somewhere else; the challenge is tracing the symptoms back to their ultimate source.\n",
+ "
\n",
+ "\n",
+ "Next, let us take a look at the variables at or outside their bounds as well. When a solver reports an potentially infeasible solution, the most common cause is unexpected bounds violations so you should always check these first.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Display the variables with bounds violations.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 29,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Display the variables with bounds violations"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 30,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "The following variable(s) have values at or outside their bounds (tol=0.0E+00):\n",
+ "\n",
+ " v3 (free): value=0.0 bounds=(0, 5)\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.display_variables_at_or_outside_bounds()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The toolbox is telling us that ``v3`` is the variable with a potential issue. It is also showing us the current value and bounds for ``v3`` as well as if it is a fixed or free variable, which will be useful for diagnosing the issues.\n",
+ "\n",
+ "We can see that ``v3`` is a free variable with bounds between 0 and 5 and a current value of 0. As ``v3`` is a free variable, this suggests that the solver has pushed the value to the bound where it cannot go any further, and this might be part of the cause of our infeasibility.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "When dealing with bounds violations you should always start by understanding why the bounds exist and what they mean - in many cases a bound indicates the range over which the model can be trusted and that going beyond this may result in unexpected behavior due to extrapolation.\n",
+ " \n",
+ "Never arbitrarily change a bound just because it is causing your model to be infeasible without understanding the consequences of this decision. Often, a bound violation is an indication that you need to re-think some of the constraints in your model to find alternatives which are valid in the actual range of values you are trying to solve for.\n",
+ "
\n",
+ "\n",
+ "For this example, let us assume that we made a mistake with the bounds on ``v3`` and set the lower bound to be -5.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Update the bounds on v3 in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 31,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Update bounds for v3"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 32,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "m.v3.setlb(-5)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now that we have fixed the bounds issues, we should check whether our model is now feasible. However, before we continue we should recognize that we have just made a structural change to the model. If we were not careful, this could have introduced new structural issues to the model, so we should start from the beginning just to be sure.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "In general, you should always start from the beginning of the model diagnosis workflow after you make any change to the model. Remember to also record these changes in your log book in case something unexpected happens so that you can revert any changes that cause problems.\n",
+ "
\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Check to see if there are any new structural issues in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 33,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Check for new structural issues"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 34,
+ "metadata": {
+ "scrolled": true,
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Activated Blocks: 1 (Deactivated: 0)\n",
+ " Free Variables in Activated Constraints: 4 (External: 0)\n",
+ " Free Variables with only lower bounds: 0\n",
+ " Free Variables with only upper bounds: 0\n",
+ " Free Variables with upper and lower bounds: 2\n",
+ " Fixed Variables in Activated Constraints: 3 (External: 0)\n",
+ " Activated Equality Constraints: 4 (Deactivated: 0)\n",
+ " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
+ " Activated Objectives: 0 (Deactivated: 0)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "0 WARNINGS\n",
+ "\n",
+ " No warnings found!\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 Cautions\n",
+ "\n",
+ " Caution: 1 variable fixed to 0\n",
+ " Caution: 1 unused variable (0 fixed)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " Try to initialize/solve your model and then call report_numerical_issues()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Our change has not introduced any new structural issues, so we can move on and try to solve the model again.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Re-solve the model in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 35,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Re-solve the model"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 36,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "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...: 7\n",
+ "Number of nonzeros in inequality constraint Jacobian.: 0\n",
+ "Number of nonzeros in Lagrangian Hessian.............: 0\n",
+ "\n",
+ "Total number of variables............................: 4\n",
+ " variables with only lower bounds: 0\n",
+ " variables with lower and upper bounds: 2\n",
+ " variables with only upper bounds: 0\n",
+ "Total number of equality constraints.................: 4\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 6.67e-01 0.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
+ " 1 0.0000000e+00 6.66e-03 2.97e+00 -1.0 2.00e+00 - 7.17e-01 9.90e-01h 1\n",
+ " 2 0.0000000e+00 6.27e-05 9.38e+00 -1.0 2.00e-02 - 1.00e+00 9.91e-01h 1\n",
+ " 3 0.0000000e+00 8.88e-16 1.13e-12 -1.0 1.88e-04 - 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....: 8.8817841970012523e-16 8.8817841970012523e-16\n",
+ "Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Overall NLP error.......: 8.8817841970012523e-16 8.8817841970012523e-16\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"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "{'Problem': [{'Lower bound': -inf, 'Upper bound': inf, 'Number of objectives': 1, 'Number of constraints': 4, 'Number of variables': 4, 'Sense': 'unknown'}], 'Solver': [{'Status': 'ok', 'Message': 'Ipopt 3.13.2\\\\x3a Optimal Solution Found', 'Termination condition': 'optimal', 'Id': 0, 'Error rc': 0, 'Time': 0.02317023277282715}], 'Solution': [OrderedDict([('number of solutions', 0), ('number of solutions displayed', 0)])]}"
+ ]
+ },
+ "execution_count": 36,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "solver.solve(m, tee=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "IPOPT should have returned optimal solution now, so it looks like those bounds were what was causing the model to be infeasible. At this point, the model is now solving (for the current values at least), so you might think that the model is now ready for optimization.\n",
+ "\n",
+ "However, if we look at the solver logs we can see that it took around 3 iterations for IPOPT to solve our model (depending on minor variations in computer architecture). For a model this simple, we would generally expect it to solve in only 1 iteration so there is still some room for improvement.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "You should keep in mind that just because you get an optimal solution does not mean that your model is robust and free of issues.\n",
+ " \n",
+ "You should always take the time to look over the solver logs to look for signs of trouble, even if you get an optimal solution. While you might get an optimal solution for the current state, there may be advance warning signs of issues that will cause problems later when you try to solve the model at a different state.\n",
+ "
\n",
+ "\n",
+ "Let us run the ``report_numerical_issues`` method again to see if there are any other problems we need to address.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Check for additional numerical issues in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 37,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Check for additional numerical issues"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 38,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Jacobian Condition Number: 1.700E+01\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "0 WARNINGS\n",
+ "\n",
+ " No warnings found!\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "5 Cautions\n",
+ "\n",
+ " Caution: 1 Variable with value close to their bounds (abs=1.0E-04, rel=1.0E-04)\n",
+ " Caution: 1 Variable with value close to zero (tol=1.0E-08)\n",
+ " Caution: 1 Variable with extreme value (<1.0E-04 or >1.0E+04)\n",
+ " Caution: 1 Variable with None value\n",
+ " Caution: 1 extreme Jacobian Entry (<1.0E-04 or >1.0E+04)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " If you still have issues converging your model consider:\n",
+ " prepare_svd_toolbox()\n",
+ " prepare_degeneracy_hunter()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.report_numerical_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The toolbox is not reporting any warnings which is good, however there are still 5 numerical cautions that it has identified which might be contributing to the larger than expected number of iterations. As mentioned earlier, the toolbox does not suggest methods for investigating these, but there are methods available. For example, we can look at the variable with an extreme value using the `display_variables_with_extreme_values()` method.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Check for additional information about variables with extreme values.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 39,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Display variable with extreme value"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 40,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "The following variable(s) have extreme values (<1.0E-04 or > 1.0E+04):\n",
+ "\n",
+ " v7: 4.9999999999999945e-08\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.display_variables_with_extreme_values()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We can see that ``v7`` is potentially causing problems due to having a very small value (on the order of magnitude of the solver tolerance). This can be especially problematic for interior point solvers like IPOPT if there is a lower bound of 0 (which there is in this case). IPOPT tries to avoid bounds and thus perturbs solutions away from these if it gets too close, which can cause convergence to be slow (or fail) if the solution lies close to the bound.\n",
+ "\n",
+ "We can address this by scaling the variable so that the value of the scaled variable is large enough that the solution is not close to the lower bound. Additionally, we should look at any constraint that ``v7`` appears in (in this case ``c4``) and ensure that those constraints are well scaled as well (so that a residual of 1e-6 is reasonable for the terms involved).\n",
+ "\n",
+ "For this case, we can set a scaling factor of 1e8 for both ``v7`` and ``c4`` as shown below. Note that we also need to apply Pyomo's scaling transformation to create a new scaled model to work with."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 41,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.scaling_factor = pyo.Suffix(direction=pyo.Suffix.EXPORT)\n",
+ "\n",
+ "m.scaling_factor[m.v7] = 1e8\n",
+ "m.scaling_factor[m.c4] = 1e8\n",
+ "\n",
+ "scaling = pyo.TransformationFactory(\"core.scale_model\")\n",
+ "scaled_model = scaling.create_using(m, rename=False)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now that we have a scaled model, we can try to solve it and hopefully see better convergence than the unscaled model.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Solve the scaled model and check to see how many iterations are required.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 42,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Solve scaled model"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 43,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "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...: 7\n",
+ "Number of nonzeros in inequality constraint Jacobian.: 0\n",
+ "Number of nonzeros in Lagrangian Hessian.............: 0\n",
+ "\n",
+ "Total number of variables............................: 4\n",
+ " variables with only lower bounds: 0\n",
+ " variables with lower and upper bounds: 2\n",
+ " variables with only upper bounds: 0\n",
+ "Total number of equality constraints.................: 4\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 5.33e-15 0.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
+ "\n",
+ "Number of Iterations....: 0\n",
+ "\n",
+ " (scaled) (unscaled)\n",
+ "Objective...............: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Dual infeasibility......: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Constraint violation....: 5.3290705182007514e-15 5.3290705182007514e-15\n",
+ "Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Overall NLP error.......: 5.3290705182007514e-15 5.3290705182007514e-15\n",
+ "\n",
+ "\n",
+ "Number of objective function evaluations = 1\n",
+ "Number of objective gradient evaluations = 1\n",
+ "Number of equality constraint evaluations = 1\n",
+ "Number of inequality constraint evaluations = 0\n",
+ "Number of equality constraint Jacobian evaluations = 1\n",
+ "Number of inequality constraint Jacobian evaluations = 0\n",
+ "Number of Lagrangian Hessian evaluations = 0\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"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "{'Problem': [{'Lower bound': -inf, 'Upper bound': inf, 'Number of objectives': 1, 'Number of constraints': 4, 'Number of variables': 4, 'Sense': 'unknown'}], 'Solver': [{'Status': 'ok', 'Message': 'Ipopt 3.13.2\\\\x3a Optimal Solution Found', 'Termination condition': 'optimal', 'Id': 0, 'Error rc': 0, 'Time': 0.0058002471923828125}], 'Solution': [OrderedDict([('number of solutions', 0), ('number of solutions displayed', 0)])]}"
+ ]
+ },
+ "execution_count": 43,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "solver.solve(scaled_model, tee=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As we can see, the scaled model solved in 0 iterations (indicating that it already had the right solution). However, had we done this to the unscaled model we would have found it required 2-3 iterations again due to IPOPT perturbing the initial (correct) solution away from the bounds.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "Normally in these cases we would need to map the solution from the scaled model back to the unscaled model so we can view the results. In this case, we are not actually interested in the solution so we move on with the model diagnosis.\n",
+ "
\n",
+ "\n",
+ "Now that we have fixed the scaling issues, we can go back to the ``DiagnosticsToolbox`` and see if we still have any warnings. Note however that we need to look at the scaled model now rather than the original model, so we need to create a new instance of the ``DiagnosticsToolbox`` with the scaled model as the ``model`` argument.\n",
+ "\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Create a new instance of the DiagnosticsToolbox and check the scaled model for issues.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 45,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Create a new diagnostics toolbox for scaled model\n",
+ "\n",
+ "# Report numerical issues for scaled model"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 46,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Jacobian Condition Number: 1.800E+01\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "0 WARNINGS\n",
+ "\n",
+ " No warnings found!\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "3 Cautions\n",
+ "\n",
+ " Caution: 1 Variable with value close to their bounds (abs=1.0E-04, rel=1.0E-04)\n",
+ " Caution: 1 Variable with value close to zero (tol=1.0E-08)\n",
+ " Caution: 1 Variable with None value\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " If you still have issues converging your model consider:\n",
+ " prepare_svd_toolbox()\n",
+ " prepare_degeneracy_hunter()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt_scaled = DiagnosticsToolbox(scaled_model)\n",
+ "dt_scaled.report_numerical_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We can see that applying scaling addressed two of the cautions we had before (the variable with an extreme value and an associated large value in the model Jacobian). Whilst we were able to solve the unscaled model in this case, this is in part because it was a simple linear model. In more complex, non-linear models, scaling becomes much more important and often depends strongly on the current state of the model. That is, you can often find cases where the unscaled (or poorly scaled) model solves for a limited range of conditions but fails to solve if you move too far away for the current state. Whilst you might be able to solve the model at the current state, you should always check the solver logs and numerical cautions for advanced warning signs of scaling issues that might manifest later when you try to solve the model for a different state (e.g., during optimization).\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "By their nature, numerical issues depend on the current values of the variables in the model, and thus may remain hidden until someone tries to solve the model close to where the issue exists. For this reason, the full model diagnostics workflow contains steps to run the numerical checks across a wide range of variable values to try to ensure that no issues remain hidden. This is beyond the scope of this tutorial however.\n",
+ "
\n",
+ "\n",
+ "At this point, we have addressed all the issues that were preventing us from solving the demonstration model and so reached the end of this tutorial. For cases where we are still having trouble solving the model, we can see that the toolbox is suggesting additional methods for further debugging and these advanced features will be the focus of separate tutorials."
+ ]
+ }
+ ],
+ "metadata": {
+ "celltoolbar": "Tags",
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
},
- "nbformat": 4,
- "nbformat_minor": 3
+ "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
}
\ No newline at end of file
diff --git a/idaes_examples/notebooks/docs/diagnostics/diagnostics_toolbox_test.ipynb b/idaes_examples/notebooks/docs/diagnostics/diagnostics_toolbox_test.ipynb
index e69345be..740f40fe 100644
--- a/idaes_examples/notebooks/docs/diagnostics/diagnostics_toolbox_test.ipynb
+++ b/idaes_examples/notebooks/docs/diagnostics/diagnostics_toolbox_test.ipynb
@@ -1,1865 +1,1868 @@
{
- "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": [
- "# IDAES Model Diagnostics Toolbox Tutorial\n",
- "Author: Andrew Lee \n",
- "Maintainer: Andrew Lee \n",
- "Updated: 2023-10-31 \n",
- "\n",
- "As you have likely discovered already, developing and solving models in an equation-oriented (EO) environment can be challenging and often takes a significant amount of effort. There are many pitfalls and mistakes that can be encountered when developing a model which can greatly impact the solvability and robustness of the final problem.\n",
- "\n",
- "Model diagnosis and debugging is often more of an art than a science, and it generally relies on significant experience and understanding both of general EO modeling techniques and the specific model and problem being solved. To assist with this process, IDAES has developed a model diagnostics toolbox that brings together a large number of tools for identifying potential issues in a model to help guide the user through the process of finding and resolving these issues. Note however that whilst these tools can help identify the presence of an issue, remedying the issue always requires some degree of engineering knowledge about the system being modeled, and thus it is ultimately up to the user to find a solution to the problem.\n",
- "\n",
- "This tutorial will take you through using the ``DiagnosticsToolbox`` to debug a number of issues in a simple Pyomo model and to take it from initially reporting a possible infeasible solution to returning the correct solution.\n",
- "\n",
- "To get started, the ``DiagnosticsToolbox`` can be imported from ``idaes.core.util``.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Import the DiagnosticsToolbox in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "from idaes.core.util import DiagnosticsToolbox"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "To get some information on where to start, try using the Python ``help()`` function to see the documentation for the ``DiagnosticsToolbox``.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Call `help(DiagnosticsToolbox)` to see some more information on the toolbox and some instructions on how to get started.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on class DiagnosticsToolbox in module idaes.core.util.model_diagnostics:\n",
- "\n",
- "class DiagnosticsToolbox(builtins.object)\n",
- " | DiagnosticsToolbox(model: pyomo.core.base.block._BlockData, **kwargs)\n",
- " | \n",
- " | The IDAES Model DiagnosticsToolbox.\n",
- " | \n",
- " | To get started:\n",
- " | \n",
- " | 1. Create an instance of your model (this does not need to be initialized yet).\n",
- " | 2. Fix variables until you have 0 degrees of freedom. Many of these tools presume\n",
- " | a square model, and a square model should always be the foundation of any more\n",
- " | advanced model.\n",
- " | 3. Create an instance of the DiagnosticsToolbox and provide the model to debug as\n",
- " | the model argument.\n",
- " | 4. Call the ``report_structural_issues()`` method.\n",
- " | \n",
- " | Model diagnostics is an iterative process and you will likely need to run these\n",
- " | tools multiple times to resolve all issues. After making a change to your model,\n",
- " | you should always start from the beginning again to ensure the change did not\n",
- " | introduce any new issues; i.e., always start from the report_structural_issues()\n",
- " | method.\n",
- " | \n",
- " | Note that structural checks do not require the model to be initialized, thus users\n",
- " | should start with these. Numerical checks require at least a partial solution to the\n",
- " | model and should only be run once all structural issues have been resolved.\n",
- " | \n",
- " | Report methods will print a summary containing three parts:\n",
- " | \n",
- " | 1. Warnings - these are critical issues that should be resolved before continuing.\n",
- " | For each warning, a method will be suggested in the Next Steps section to get\n",
- " | additional information.\n",
- " | 2. Cautions - these are things that could be correct but could also be the source of\n",
- " | solver issues. Not all cautions need to be addressed, but users should investigate\n",
- " | each one to ensure that the behavior is correct and that they will not be the source\n",
- " | of difficulties later. Methods exist to provide more information on all cautions,\n",
- " | but these will not appear in the Next Steps section.\n",
- " | 3. Next Steps - these are recommended methods to call from the DiagnosticsToolbox to\n",
- " | get further information on warnings. If no warnings are found, this will suggest\n",
- " | the next report method to call.\n",
- " | \n",
- " | Args:\n",
- " | \n",
- " | model: model to be diagnosed. The DiagnosticsToolbox does not support indexed Blocks.\n",
- " | \n",
- " | Keyword Arguments\n",
- " | -----------------\n",
- " | variable_bounds_absolute_tolerance: float, default=0.0001\n",
- " | Absolute tolerance for considering a variable to be close to its\n",
- " | bounds.\n",
- " | \n",
- " | variable_bounds_relative_tolerance: float, default=0.0001\n",
- " | Relative tolerance for considering a variable to be close to its\n",
- " | bounds.\n",
- " | \n",
- " | variable_bounds_violation_tolerance: float, default=0\n",
- " | Absolute tolerance for considering a variable to violate its bounds.\n",
- " | Some solvers relax bounds on variables thus allowing a small violation\n",
- " | to be considered acceptable.\n",
- " | \n",
- " | constraint_residual_tolerance: float, default=1e-05\n",
- " | Absolute tolerance to use when checking constraint residuals.\n",
- " | \n",
- " | variable_large_value_tolerance: float, default=10000.0\n",
- " | Absolute tolerance for considering a value to be large.\n",
- " | \n",
- " | variable_small_value_tolerance: float, default=0.0001\n",
- " | Absolute tolerance for considering a value to be small.\n",
- " | \n",
- " | variable_zero_value_tolerance: float, default=1e-08\n",
- " | Absolute tolerance for considering a value to be near to zero.\n",
- " | \n",
- " | jacobian_large_value_caution: float, default=10000.0\n",
- " | Tolerance for raising a caution for large Jacobian values.\n",
- " | \n",
- " | jacobian_large_value_warning: float, default=100000000.0\n",
- " | Tolerance for raising a warning for large Jacobian values.\n",
- " | \n",
- " | jacobian_small_value_caution: float, default=0.0001\n",
- " | Tolerance for raising a caution for small Jacobian values.\n",
- " | \n",
- " | jacobian_small_value_warning: float, default=1e-08\n",
- " | Tolerance for raising a warning for small Jacobian values.\n",
- " | \n",
- " | warn_for_evaluation_error_at_bounds: bool, default=True\n",
- " | If False, warnings will not be generated for things like log(x) with x\n",
- " | >= 0\n",
- " | \n",
- " | Methods defined here:\n",
- " | \n",
- " | __init__(self, model: pyomo.core.base.block._BlockData, **kwargs)\n",
- " | Initialize self. See help(type(self)) for accurate signature.\n",
- " | \n",
- " | assert_no_numerical_warnings(self)\n",
- " | Checks for numerical warnings in the model and raises an AssertionError\n",
- " | if any are found.\n",
- " | \n",
- " | Raises:\n",
- " | AssertionError if any warnings are identified by numerical analysis.\n",
- " | \n",
- " | assert_no_structural_warnings(self)\n",
- " | Checks for structural warnings in the model and raises an AssertionError\n",
- " | if any are found.\n",
- " | \n",
- " | Raises:\n",
- " | AssertionError if any warnings are identified by structural analysis.\n",
- " | \n",
- " | display_components_with_inconsistent_units(self, stream=None)\n",
- " | Prints a list of all Constraints, Expressions and Objectives in the\n",
- " | model with inconsistent units of measurement.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_constraints_with_extreme_jacobians(self, stream=None)\n",
- " | Prints the constraints associated with rows in the Jacobian with extreme\n",
- " | L2 norms. This often indicates poorly scaled constraints.\n",
- " | \n",
- " | Tolerances can be set via the DiagnosticsToolbox config.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the output to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_constraints_with_large_residuals(self, stream=None)\n",
- " | Prints a list of Constraints with residuals greater than a specified tolerance.\n",
- " | Tolerance can be set in the class configuration options.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_external_variables(self, stream=None)\n",
- " | Prints a list of variables that appear within activated Constraints in the\n",
- " | model but are not contained within the model themselves.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_extreme_jacobian_entries(self, stream=None)\n",
- " | Prints variables and constraints associated with entries in the Jacobian with extreme\n",
- " | values. This can be indicative of poor scaling, especially for isolated terms (e.g.\n",
- " | variables which appear only in one term of a single constraint).\n",
- " | \n",
- " | Tolerances can be set via the DiagnosticsToolbox config.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the output to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_overconstrained_set(self, stream=None)\n",
- " | Prints the variables and constraints in the over-constrained sub-problem\n",
- " | from a Dulmage-Mendelsohn partitioning.\n",
- " | \n",
- " | This can be used to identify the over-defined part of a model and thus\n",
- " | where constraints must be removed or variables unfixed.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_potential_evaluation_errors(self, stream=None)\n",
- " | Prints constraints that may be prone to evaluation errors\n",
- " | (e.g., log of a negative number) based on variable bounds.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the output to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_underconstrained_set(self, stream=None)\n",
- " | Prints the variables and constraints in the under-constrained sub-problem\n",
- " | from a Dulmage-Mendelsohn partitioning.\n",
- " | \n",
- " | This can be used to identify the under-defined part of a model and thus\n",
- " | where additional information (fixed variables or constraints) are required.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_unused_variables(self, stream=None)\n",
- " | Prints a list of variables that do not appear in any activated Constraints.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_variables_at_or_outside_bounds(self, stream=None)\n",
- " | Prints a list of variables with values that fall at or outside the bounds\n",
- " | on the variable.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_variables_fixed_to_zero(self, stream=None)\n",
- " | Prints a list of variables that are fixed to an absolute value of 0.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_variables_near_bounds(self, stream=None)\n",
- " | Prints a list of variables with values close to their bounds. Tolerance can\n",
- " | be set in the class configuration options.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_variables_with_extreme_jacobians(self, stream=None)\n",
- " | Prints the variables associated with columns in the Jacobian with extreme\n",
- " | L2 norms. This often indicates poorly scaled variables.\n",
- " | \n",
- " | Tolerances can be set via the DiagnosticsToolbox config.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the output to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_variables_with_extreme_values(self, stream=None)\n",
- " | Prints a list of variables with extreme values.\n",
- " | \n",
- " | Tolerances can be set in the class configuration options.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_variables_with_none_value(self, stream=None)\n",
- " | Prints a list of variables with a value of None.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_variables_with_value_near_zero(self, stream=None)\n",
- " | Prints a list of variables with a value close to zero. The tolerance\n",
- " | for determining what is close to zero can be set in the class configuration\n",
- " | options.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | get_dulmage_mendelsohn_partition(self)\n",
- " | Performs a Dulmage-Mendelsohn partitioning on the model and returns\n",
- " | the over- and under-constrained sub-problems.\n",
- " | \n",
- " | Returns:\n",
- " | list-of-lists variables in each independent block of the under-constrained set\n",
- " | list-of-lists constraints in each independent block of the under-constrained set\n",
- " | list-of-lists variables in each independent block of the over-constrained set\n",
- " | list-of-lists constraints in each independent block of the over-constrained set\n",
- " | \n",
- " | prepare_degeneracy_hunter(self, **kwargs)\n",
- " | Create an instance of the DegeneracyHunter and store as self.degeneracy_hunter.\n",
- " | \n",
- " | After creating an instance of the toolbox, call\n",
- " | report_irreducible_degenerate_sets.\n",
- " | \n",
- " | Returns:\n",
- " | \n",
- " | Instance of DegeneracyHunter\n",
- " | \n",
- " | Keyword Arguments\n",
- " | -----------------\n",
- " | solver: str, default='scip'\n",
- " | MILP solver to use for finding irreducible degenerate sets.\n",
- " | \n",
- " | solver_options: optional\n",
- " | Options to pass to MILP solver.\n",
- " | \n",
- " | M: float, default=100000.0\n",
- " | Maximum value for nu in MILP models.\n",
- " | \n",
- " | m_small: float, default=1e-05\n",
- " | Smallest value for nu to be considered non-zero in MILP models.\n",
- " | \n",
- " | trivial_constraint_tolerance: float, default=1e-06\n",
- " | Tolerance for identifying non-zero rows in Jacobian.\n",
- " | \n",
- " | prepare_svd_toolbox(self, **kwargs)\n",
- " | Create an instance of the SVDToolbox and store as self.svd_toolbox.\n",
- " | \n",
- " | After creating an instance of the toolbox, call\n",
- " | display_underdetermined_variables_and_constraints().\n",
- " | \n",
- " | Returns:\n",
- " | \n",
- " | Instance of SVDToolbox\n",
- " | \n",
- " | Keyword Arguments\n",
- " | -----------------\n",
- " | number_of_smallest_singular_values: PositiveInt, optional\n",
- " | Number of smallest singular values to compute\n",
- " | \n",
- " | svd_callback: svd_callback_validator, default=\n",
- " | Callback to SVD method of choice (default = svd_dense). Callbacks\n",
- " | should take the Jacobian and number of singular values to compute as\n",
- " | options, plus any method specific arguments, and should return the u,\n",
- " | s and v matrices as numpy arrays.\n",
- " | \n",
- " | svd_callback_arguments: dict, optional\n",
- " | Optional arguments to pass to SVD callback (default = None)\n",
- " | \n",
- " | singular_value_tolerance: float, default=1e-06\n",
- " | Tolerance for defining a small singular value\n",
- " | \n",
- " | size_cutoff_in_singular_vector: float, default=0.1\n",
- " | Size below which to ignore constraints and variables in the singular\n",
- " | vector\n",
- " | \n",
- " | report_numerical_issues(self, stream=None)\n",
- " | Generates a summary report of any numerical issues identified in the model provided\n",
- " | and suggest next steps for debugging model.\n",
- " | \n",
- " | Numerical checks should only be performed once all structural issues have been resolved,\n",
- " | and require that at least a partial solution to the model is available.\n",
- " | \n",
- " | Args:\n",
- " | stream: I/O object to write report to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | report_structural_issues(self, stream=None)\n",
- " | Generates a summary report of any structural issues identified in the model provided\n",
- " | and suggests next steps for debugging the model.\n",
- " | \n",
- " | This should be the first method called when debugging a model and after any change\n",
- " | is made to the model. These checks can be run before trying to initialize and solve\n",
- " | the model.\n",
- " | \n",
- " | Args:\n",
- " | stream: I/O object to write report to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | ----------------------------------------------------------------------\n",
- " | Readonly properties defined here:\n",
- " | \n",
- " | model\n",
- " | Model currently being diagnosed.\n",
- " | \n",
- " | ----------------------------------------------------------------------\n",
- " | Data descriptors defined here:\n",
- " | \n",
- " | __dict__\n",
- " | dictionary for instance variables (if defined)\n",
- " | \n",
- " | __weakref__\n",
- " | list of weak references to the object (if defined)\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(DiagnosticsToolbox)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The ``help()`` function gives us a lot of information on the ``DiagnosticsToolbox`` and all the methods that it supports (and there are many). However, the important part to start with are the four steps outlined at the top of the doc string that tell us how to get started.\n",
- "\n",
- "Firstly, we need a model to test (and, for this tutorial at least, one that has a wide range of issues that we need to fix before it will solve). We then also need to fix some variables so that we have 0 degrees of freedom in our model. Whilst our ultimate goal is generally optimization (and thus a system with 1 or more degrees of freedom), all models conceptually derive from a square model representing a nominal state. If this nominal state is not well-posed, then any issues present will also be present in the resulting optimization (even if adding degrees of freedom means that the model is now easier to solve).\n",
- "\n",
- "The cell below contains a demonstration model for this tutorial that contains a number of issues that we will resolve using the ``DiagnosticsToolbox``."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [],
- "source": [
- "import pyomo.environ as pyo\n",
- "\n",
- "m = pyo.ConcreteModel()\n",
- "\n",
- "m.v1 = pyo.Var(units=pyo.units.m)\n",
- "m.v2 = pyo.Var(units=pyo.units.m)\n",
- "m.v3 = pyo.Var(bounds=(0, 5))\n",
- "m.v4 = pyo.Var()\n",
- "m.v5 = pyo.Var(bounds=(0, 10))\n",
- "m.v6 = pyo.Var()\n",
- "m.v7 = pyo.Var(units=pyo.units.m, bounds=(0, 1)) # Poorly scaled variable with lower bound\n",
- "m.v8 = pyo.Var() # unused variable\n",
- "\n",
- "m.c1 = pyo.Constraint(expr=m.v1 + m.v2 == 10) # Unit consistency issue\n",
- "m.c2 = pyo.Constraint(expr=m.v3 == m.v4 + m.v5)\n",
- "m.c3 = pyo.Constraint(expr=2*m.v3 == 3*m.v4 + 4*m.v5 + m.v6)\n",
- "m.c4 = pyo.Constraint(expr=m.v7 == 1e-8*m.v1) # Poorly scaled constraint\n",
- "\n",
- "m.v4.fix(2)\n",
- "m.v5.fix(2)\n",
- "m.v6.fix(0)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Next, the instructions tell us to create an instance of the ``DiagnosticsToolbox`` and to pass the model we wish to examine as an argument.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Create an instance of the DiagnosticsToolbox: dt = DiagnosticsToolbox(m)\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "dt = DiagnosticsToolbox(m)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Finally, the instructions tell us to run the ``report_structural_issues()`` method. Structural issues represent issues that exist solely in the form of the model equations and thus do not depend on the current value of any of the variables. This is useful as it means we can check for these before we even call a solver, which can be critical as sometimes these issues will cause a solver to fail without providing a useful solution.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Call dt.report_structural_issues() in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Model Statistics\n",
- "\n",
- " Activated Blocks: 1 (Deactivated: 0)\n",
- " Free Variables in Activated Constraints: 4 (External: 0)\n",
- " Free Variables with only lower bounds: 0\n",
- " Free Variables with only upper bounds: 0\n",
- " Free Variables with upper and lower bounds: 2\n",
- " Fixed Variables in Activated Constraints: 3 (External: 0)\n",
- " Activated Equality Constraints: 4 (Deactivated: 0)\n",
- " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
- " Activated Objectives: 0 (Deactivated: 0)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "2 WARNINGS\n",
- "\n",
- " WARNING: 1 Component with inconsistent units\n",
- " WARNING: Structural singularity found\n",
- " Under-Constrained Set: 3 variables, 2 constraints\n",
- " Over-Constrained Set: 1 variables, 2 constraints\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "2 Cautions\n",
- "\n",
- " Caution: 1 variable fixed to 0\n",
- " Caution: 1 unused variable (0 fixed)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "Suggested next steps:\n",
- "\n",
- " display_components_with_inconsistent_units()\n",
- " display_underconstrained_set()\n",
- " display_overconstrained_set()\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.report_structural_issues()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Looking at the output from the ``report_structural_issues()`` method, we can see that it provides a fairly short summary containing 4 sections.\n",
- "\n",
- "1. The first section is a summary of the size of the model, indicating things like the number of variables and constraints. The size of the model is often important for judging how difficult it will be to solve, and this information can also be useful for comparison to what is being sent to the solver. Most solvers will report the size of the model in their output logs, and if there is a difference between what is reported here and by the solver, then you should probably look into what is happening. This section also notes some things such as if you have any deactivated Blocks, Constraints or Objectives, or if you have variables which appear in the constraints that are not part of the model; these are not necessarily wrong but it is easy to have accidentally deactivated something you did not intend to so you should always check to see that these are expected.\n",
- "\n",
- "2. The second section provides a summary of any critical structural issues that were found - in this case we can see that there are 2 warnings we are going to need to look into. Warnings represent issues that need to be addressed before moving on as these will likely cause the solver to fail or give an incorrect answer.\n",
- "\n",
- "3. The third section lists a summary of any cautions that are found. Cautions represent issues that may or may not be problematic; in many cases these might be expected behaviors or borderline issues. However, these could also represent conceptual issues that should be addressed, so users should take the time to investigate these and determine if they need to be fixed or not.\n",
- "\n",
- "4. Finally, there is a section that suggests the next steps to take to help guide you through the model diagnosis process. If any warnings were identified, this section will list methods that can help you get more information on each specific problem, and if no warnings are found then it will guide you onto the next step in the model diagnosis workflow.\n",
- "\n",
- "**Note:** there are methods available to help investigate cautions as well, but these will not show up in the next steps in order to avoid cluttering the output. You can get more information on the available methods for investigating cautions via the documentation or ``help()`` function.\n",
- "\n",
- "In our current model, we have 2 critical issues (warnings) that we need to look into and resolve. The order in which we resolve these will generally not matter, but be aware that these can often be interrelated - fixing one warning might resolve other warnings as well (or create new ones), and sometimes you will need to look at multiple issues together to find the overall root cause.\n",
- "\n",
- "To start with, let us look at the unit consistency issue. From the \"Next Steps\" section above, the toolbox is suggesting we run the ``display_components_with_inconsistent_units()`` method for more information.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Call the `display_components_with_inconsistent_units()` method from the DiagnosticsToolbox to see more information on which constraint is causing the unit consistency issues.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 11,
- "metadata": {
- "scrolled": false,
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "The following component(s) have unit consistency issues:\n",
- "\n",
- " c1\n",
- "\n",
- "For more details on unit inconsistencies, import the assert_units_consistent method\n",
- "from pyomo.util.check_units\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.display_components_with_inconsistent_units()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "This tells us that the issue lies in constraint ``c1``. If we go back and look at this constraint, we can see that it says ``v1 + v2 == 10``. ``v1`` and ``v2`` both have units of ``m`` which is consistent, but the constant in the expression (right hand side) is unitless. Thus, we need to correct this so that the right hand side has units for the constraint to be consistent.\n",
- "\n",
- "The cell below shows how to delete a constraint and replace it with a new one with the correct units.\n",
- "\n",
- "\n",
- "Warning:\n",
- "Deleting components can cause unexpected issues if something else in a model is using that component (e.g., deleting a variable which is used in a constraint). You should always be careful when deleting Pyomo components and make sure you only delete components that are not used elsewhere.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 12,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Delete the incorrect Constraint\n",
- "m.del_component(m.c1)\n",
- "\n",
- "# Re-create the Constraint with the correct units\n",
- "m.c1 = pyo.Constraint(expr=m.v1 + m.v2 == 10*pyo.units.m)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Warning:\n",
- "Fixing issues in models is often an iterative process requiring trial and error. You might also have some results from a model before running the diagnostics tools and the changes you make during debugging may make it difficult to replicate those results afterwards.\n",
- " \n",
- "It is strongly recommended that you keep a record of the changes you make at each step and why, along with a Git hash (or similar version control marker) corresponding to these changes. This will allow you see what changes and why, and give you a way to go back to previous iterations if the current approach does not work out. The IDAES documentation contains recommendations on how to keep and maintain a modeling logbook.\n",
- "
\n",
- "\n",
- "Now, re-run the ``report_structural_issues()`` method and see if this change has fixed the unit consistency issue.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Call dt.report_structural_issues() in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 14,
- "metadata": {
- "scrolled": true,
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Model Statistics\n",
- "\n",
- " Activated Blocks: 1 (Deactivated: 0)\n",
- " Free Variables in Activated Constraints: 4 (External: 0)\n",
- " Free Variables with only lower bounds: 0\n",
- " Free Variables with only upper bounds: 0\n",
- " Free Variables with upper and lower bounds: 2\n",
- " Fixed Variables in Activated Constraints: 3 (External: 0)\n",
- " Activated Equality Constraints: 4 (Deactivated: 0)\n",
- " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
- " Activated Objectives: 0 (Deactivated: 0)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "1 WARNINGS\n",
- "\n",
- " WARNING: Structural singularity found\n",
- " Under-Constrained Set: 3 variables, 2 constraints\n",
- " Over-Constrained Set: 1 variables, 2 constraints\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "2 Cautions\n",
- "\n",
- " Caution: 1 variable fixed to 0\n",
- " Caution: 1 unused variable (0 fixed)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "Suggested next steps:\n",
- "\n",
- " display_underconstrained_set()\n",
- " display_overconstrained_set()\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.report_structural_issues()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The unit consistency issue has been resolved by the changes above, so now we need to look at the structural singularity. A structural singularity occurs when one sub-part of the model is over-constrained (negative degrees of freedom), which generally means another part is under-constrained (positive degrees of freedom, assuming that there are 0 degrees of freedom overall).\n",
- "\n",
- "The toolbox is suggesting we use the ``display_overconstrained_set()`` and ``display_underconstrained_set()`` methods to get more information on the singularity; for now, let us start with the over-constrained set.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Call dt.display_overconstrained_set() in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 16,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Dulmage-Mendelsohn Over-Constrained Set\n",
- "\n",
- " Independent Block 0:\n",
- "\n",
- " Variables:\n",
- "\n",
- " v3\n",
- "\n",
- " Constraints:\n",
- "\n",
- " c2\n",
- " c3\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.display_overconstrained_set()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "From the output above, the toolbox is telling us that we have two constraints (``c2`` and ``c3``) which only contain a single unfixed variable (``v3``); thus in this part of the model we have -1 degree of freedom and the model is not well defined (structurally singular). If we go back and look at these constraints, we can see the that the constraints are:\n",
- "\n",
- "``c2: v3 == v4 + v5``\n",
- "\n",
- "``c3: 2*v3 == 3*v4 + 4*v5 + v6``\n",
- "\n",
- "We can see that in addition to ``v3`` these constraints actually contain 3 other variables (``v4``, ``v5`` and ``v6``), however these are all variables we fixed to get our initial zero degrees of freedom. It looks like we have either accidentally fixed one too many variables or written one too many constraints.\n",
- "\n",
- "For this example, let us assume that ``v4`` was not supposed to be fixed and unfix it.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Resolve the structural singularity and then call dt.report_structural_issues() in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 18,
- "metadata": {
- "scrolled": true,
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Model Statistics\n",
- "\n",
- " Activated Blocks: 1 (Deactivated: 0)\n",
- " Free Variables in Activated Constraints: 5 (External: 0)\n",
- " Free Variables with only lower bounds: 0\n",
- " Free Variables with only upper bounds: 0\n",
- " Free Variables with upper and lower bounds: 2\n",
- " Fixed Variables in Activated Constraints: 2 (External: 0)\n",
- " Activated Equality Constraints: 4 (Deactivated: 0)\n",
- " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
- " Activated Objectives: 0 (Deactivated: 0)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "2 WARNINGS\n",
- "\n",
- " WARNING: 1 Degree of Freedom\n",
- " WARNING: Structural singularity found\n",
- " Under-Constrained Set: 3 variables, 2 constraints\n",
- " Over-Constrained Set: 0 variables, 0 constraints\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "2 Cautions\n",
- "\n",
- " Caution: 1 variable fixed to 0\n",
- " Caution: 1 unused variable (0 fixed)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "Suggested next steps:\n",
- "\n",
- " display_underconstrained_set()\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "m.v4.unfix()\n",
- "\n",
- "dt.report_structural_issues()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We can see that the over-constrained set is now empty (0 variables and 0 constraints) but the under-constrained set still has 3 variables and only 2 constraints. We can also see that there is a new warning about having 1 degree of freedom in the model, however this should not be surprising as we have just unfixed ``v4`` to resolve the over-constrained set so we have added a degree of freedom to the model.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Display the under-constrained set in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 20,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Dulmage-Mendelsohn Under-Constrained Set\n",
- "\n",
- " Independent Block 0:\n",
- "\n",
- " Variables:\n",
- "\n",
- " v2\n",
- " v1\n",
- " v7\n",
- "\n",
- " Constraints:\n",
- "\n",
- " c1\n",
- " c4\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.display_underconstrained_set()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Looking at the output from the ``display_underconstrained_set()`` method, we can see that we have two constraints, ``c1`` and ``c4``, which contain three unfixed variables, ``v1``, ``v2`` and ``v7``. Thus, we have one degree of freedom that needs to be addressed. To fix this, we could either fix one of the variables shown or add an additional equality constraint to the model.\n",
- "\n",
- "For this example let's fix ``v2`` to a value of 5 and then re-run the ``report_structural_issues()`` method.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Fix v2 to a value of 5 and then re-run dt.report_structural_issues.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 22,
- "metadata": {
- "scrolled": true,
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Model Statistics\n",
- "\n",
- " Activated Blocks: 1 (Deactivated: 0)\n",
- " Free Variables in Activated Constraints: 4 (External: 0)\n",
- " Free Variables with only lower bounds: 0\n",
- " Free Variables with only upper bounds: 0\n",
- " Free Variables with upper and lower bounds: 2\n",
- " Fixed Variables in Activated Constraints: 3 (External: 0)\n",
- " Activated Equality Constraints: 4 (Deactivated: 0)\n",
- " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
- " Activated Objectives: 0 (Deactivated: 0)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "0 WARNINGS\n",
- "\n",
- " No warnings found!\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "2 Cautions\n",
- "\n",
- " Caution: 1 variable fixed to 0\n",
- " Caution: 1 unused variable (0 fixed)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "Suggested next steps:\n",
- "\n",
- " Try to initialize/solve your model and then call report_numerical_issues()\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "m.v2.fix(5)\n",
- "\n",
- "dt.report_structural_issues()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The toolbox is now telling us that no warnings were found, so we have resolved all the structural issues (for now at least). The toolbox is telling us that there are also 2 non-critical issues (cautions) that we should look at; one about an unused variable and one about a variable fixed to zero. If you wish, you can look into identifying and fixing these yourself, however for this example we will move on to the next step (remember that the toolbox has methods to display more details for each of these which you can find in the documentation or from the ``help()`` function).\n",
- "\n",
- "For the Next Steps section, the toolbox is recommending we try to solve our model and then check for numerical issues.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Use the Pyomo SolverFactory to create an instance of IPOPT and then try to solve the model. Make sure to set \"tee=True\" as this is going to fail (and it is always good practice to review the solver logs).\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 24,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "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...: 7\n",
- "Number of nonzeros in inequality constraint Jacobian.: 0\n",
- "Number of nonzeros in Lagrangian Hessian.............: 0\n",
- "\n",
- "Total number of variables............................: 4\n",
- " variables with only lower bounds: 0\n",
- " variables with lower and upper bounds: 2\n",
- " variables with only upper bounds: 0\n",
- "Total number of equality constraints.................: 4\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 1.40e+01 0.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
- " 1 0.0000000e+00 1.39e+01 1.50e+02 -1.0 6.00e+00 - 7.16e-01 4.93e-03h 1\n",
- " 2 0.0000000e+00 1.39e+01 3.03e+06 -1.0 5.97e+00 - 1.00e+00 4.95e-05h 1\n",
- " 3r 0.0000000e+00 1.39e+01 1.00e+03 1.1 0.00e+00 - 0.00e+00 2.47e-07R 2\n",
- " 4r 0.0000000e+00 4.19e+00 9.42e+02 1.1 3.50e+03 - 4.02e-01 3.37e-03f 1\n",
- " 5r 0.0000000e+00 2.12e+00 8.72e+02 1.1 5.89e+01 - 4.35e-01 7.06e-02f 1\n",
- " 6r 0.0000000e+00 6.74e-01 6.06e+02 1.1 5.29e+00 - 9.93e-03 3.98e-01f 1\n",
- " 7r 0.0000000e+00 6.80e-01 3.14e+02 0.4 2.05e-01 - 1.00e+00 1.03e-01f 1\n",
- " 8r 0.0000000e+00 6.69e-01 2.78e-05 0.4 2.58e-02 - 1.00e+00 1.00e+00f 1\n",
- " 9r 0.0000000e+00 6.67e-01 7.56e+00 -1.7 8.13e-03 - 9.93e-01 9.96e-01f 1\n",
- "iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n",
- " 10r 0.0000000e+00 6.67e-01 2.23e-07 -1.7 4.13e-05 - 1.00e+00 1.00e+00f 1\n",
- " 11r 0.0000000e+00 6.67e-01 6.73e-01 -3.7 6.61e-05 - 1.00e+00 1.00e+00f 1\n",
- " 12r 0.0000000e+00 6.67e-01 1.91e-09 -3.7 1.48e-09 - 1.00e+00 1.00e+00h 1\n",
- " 13r 0.0000000e+00 6.67e-01 2.69e+00 -8.4 5.74e-07 - 1.00e+00 9.26e-01f 1\n",
- " 14r 0.0000000e+00 6.67e-01 7.65e+01 -8.4 4.23e-08 - 8.68e-01 1.00e+00f 1\n",
- "\n",
- "Number of Iterations....: 14\n",
- "\n",
- " (scaled) (unscaled)\n",
- "Objective...............: 0.0000000000000000e+00 0.0000000000000000e+00\n",
- "Dual infeasibility......: 3.2644919411246030e-04 3.2644919411246030e-04\n",
- "Constraint violation....: 6.6666666333656233e-01 6.6666666333656233e-01\n",
- "Complementarity.........: 4.6615546565561981e-09 4.6615546565561981e-09\n",
- "Overall NLP error.......: 6.6666666333656233e-01 6.6666666333656233e-01\n",
- "\n",
- "\n",
- "Number of objective function evaluations = 18\n",
- "Number of objective gradient evaluations = 5\n",
- "Number of equality constraint evaluations = 18\n",
- "Number of inequality constraint evaluations = 0\n",
- "Number of equality constraint Jacobian evaluations = 17\n",
- "Number of inequality constraint Jacobian evaluations = 0\n",
- "Number of Lagrangian Hessian evaluations = 15\n",
- "Total CPU secs in IPOPT (w/o function evaluations) = 0.003\n",
- "Total CPU secs in NLP function evaluations = 0.000\n",
- "\n",
- "EXIT: Converged to a point of local infeasibility. Problem may be infeasible.\n",
- "WARNING: Loading a SolverResults object with a warning status into\n",
- "model.name=\"unknown\";\n",
- " - termination condition: infeasible\n",
- " - message from solver: Ipopt 3.13.2\\x3a Converged to a locally infeasible\n",
- " point. Problem may be infeasible.\n"
- ]
- },
- {
- "data": {
- "text/plain": [
- "{'Problem': [{'Lower bound': -inf, 'Upper bound': inf, 'Number of objectives': 1, 'Number of constraints': 4, 'Number of variables': 4, 'Sense': 'unknown'}], 'Solver': [{'Status': 'warning', 'Message': 'Ipopt 3.13.2\\\\x3a Converged to a locally infeasible point. Problem may be infeasible.', 'Termination condition': 'infeasible', 'Id': 200, 'Error rc': 0, 'Time': 0.007064104080200195}], 'Solution': [OrderedDict([('number of solutions', 0), ('number of solutions displayed', 0)])]}"
- ]
- },
- "execution_count": 24,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "solver = pyo.SolverFactory('ipopt')\n",
- "solver.solve(m, tee=True)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "As hinted at above, IPOPT has returned a warning that the problem may be infeasible. Before moving on however, it is always good practice to look over the solver outputs and see what it is telling you.\n",
- "\n",
- "\n",
- "Warning:\n",
- "A lot of useful information is contained in the solver logs which is extremely useful when diagnosing modeling issues. Each solver has its own way of reporting output and its own specific behavior, so you will need to learn to interpret the output of each solver you use. The IDAES Documentation contains some guidance on interpreting output logs for a few common solvers.\n",
- "
\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Call the report_numerical_issues method in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 26,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Model Statistics\n",
- "\n",
- " Jacobian Condition Number: 1.700E+01\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "2 WARNINGS\n",
- "\n",
- " WARNING: 1 Constraint with large residuals (>1.0E-05)\n",
- " WARNING: 1 Variable at or outside bounds (tol=0.0E+00)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "5 Cautions\n",
- "\n",
- " Caution: 2 Variables with value close to their bounds (abs=1.0E-04, rel=1.0E-04)\n",
- " Caution: 2 Variables with value close to zero (tol=1.0E-08)\n",
- " Caution: 1 Variable with extreme value (<1.0E-04 or >1.0E+04)\n",
- " Caution: 1 Variable with None value\n",
- " Caution: 1 extreme Jacobian Entry (<1.0E-04 or >1.0E+04)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "Suggested next steps:\n",
- "\n",
- " display_constraints_with_large_residuals()\n",
- " display_variables_at_or_outside_bounds()\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.report_numerical_issues()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The ``report_numerical_issues()`` provides a summary similar to that which we saw for the structural issues. Firstly, it reports to us the Jacobian condition number for our problem which can give us an idea of how well-scaled the problem is, followed by a list of warnings, cautions and suggested next steps.\n",
- "\n",
- "Unsurprisingly, we are seeing a warning about a constraint with a large residual which we would expect when a solver reports a potentially infeasible problem. We are also seeing a warning about a variable with bound violations which might be contributing to the potential infeasibility.\n",
- "\n",
- "For the next steps, the toolbox is suggesting some new methods to get more information on these issues; let us start by looking at the constraints with large residuals.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Display the constraint with a large residual in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 28,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "The following constraint(s) have large residuals (>1.0E-05):\n",
- "\n",
- " c2: 6.66667E-01\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.display_constraints_with_large_residuals()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The toolbox is telling us that the constraint which failed to converge is ``c2``, however this is generally only part of the story. Solvers work by trying to minimize the infeasibility in the model (residual of the constraints), which generally means they push any infeasibility onto the least sensitive constraint in the problem. Thus, the constraint which shows the infeasibility is often not the root cause of the problem, but only the symptom of the underlying issue.\n",
- "\n",
- "If we look back at the constraints, we can see that the same variables also appear in ``c3`` and that some of these have bounds, all of which could be contributing to the infeasibility. In this case the solver tried to minimize the residual in all the constraints and ended up pushing all the issues off onto ``c2``.\n",
- "\n",
- "\n",
- "Warning:\n",
- "When dealing with solver issues such as this, you should always remember that the obvious symptoms are often just the tip of the iceberg and that the real issue generally lies somewhere else; the challenge is tracing the symptoms back to their ultimate source.\n",
- "
\n",
- "\n",
- "Next, let us take a look at the variables at or outside their bounds as well. When a solver reports an potentially infeasible solution, the most common cause is unexpected bounds violations so you should always check these first.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Display the variables with bounds violations.\n",
- "
"
- ]
- },
+ "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": [
+ "# IDAES Model Diagnostics Toolbox Tutorial\n",
+ "Author: Andrew Lee \n",
+ "Maintainer: Andrew Lee \n",
+ "Updated: 2023-10-31 \n",
+ "\n",
+ "As you have likely discovered already, developing and solving models in an equation-oriented (EO) environment can be challenging and often takes a significant amount of effort. There are many pitfalls and mistakes that can be encountered when developing a model which can greatly impact the solvability and robustness of the final problem.\n",
+ "\n",
+ "Model diagnosis and debugging is often more of an art than a science, and it generally relies on significant experience and understanding both of general EO modeling techniques and the specific model and problem being solved. To assist with this process, IDAES has developed a model diagnostics toolbox that brings together a large number of tools for identifying potential issues in a model to help guide the user through the process of finding and resolving these issues. Note however that whilst these tools can help identify the presence of an issue, remedying the issue always requires some degree of engineering knowledge about the system being modeled, and thus it is ultimately up to the user to find a solution to the problem.\n",
+ "\n",
+ "This tutorial will take you through using the {py:class}`DiagnosticsToolbox ` to debug a number of issues in a simple Pyomo model and to take it from initially reporting a possible infeasible solution to returning the correct solution.\n",
+ "\n",
+ "To get started, the ``DiagnosticsToolbox`` can be imported from ``idaes.core.util``.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Import the DiagnosticsToolbox in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from idaes.core.util import DiagnosticsToolbox"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "To get some information on where to start, try using the Python ``help()`` function to see the documentation for the ``DiagnosticsToolbox``.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Call `help(DiagnosticsToolbox)` to see some more information on the toolbox and some instructions on how to get started.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "code",
- "execution_count": 30,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "The following variable(s) have values at or outside their bounds (tol=0.0E+00):\n",
- "\n",
- " v3 (free): value=0.0 bounds=(0, 5)\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.display_variables_at_or_outside_bounds()"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Help on class DiagnosticsToolbox in module idaes.core.util.model_diagnostics:\n",
+ "\n",
+ "class DiagnosticsToolbox(builtins.object)\n",
+ " | DiagnosticsToolbox(model: pyomo.core.base.block._BlockData, **kwargs)\n",
+ " | \n",
+ " | The IDAES Model DiagnosticsToolbox.\n",
+ " | \n",
+ " | To get started:\n",
+ " | \n",
+ " | 1. Create an instance of your model (this does not need to be initialized yet).\n",
+ " | 2. Fix variables until you have 0 degrees of freedom. Many of these tools presume\n",
+ " | a square model, and a square model should always be the foundation of any more\n",
+ " | advanced model.\n",
+ " | 3. Create an instance of the DiagnosticsToolbox and provide the model to debug as\n",
+ " | the model argument.\n",
+ " | 4. Call the ``report_structural_issues()`` method.\n",
+ " | \n",
+ " | Model diagnostics is an iterative process and you will likely need to run these\n",
+ " | tools multiple times to resolve all issues. After making a change to your model,\n",
+ " | you should always start from the beginning again to ensure the change did not\n",
+ " | introduce any new issues; i.e., always start from the report_structural_issues()\n",
+ " | method.\n",
+ " | \n",
+ " | Note that structural checks do not require the model to be initialized, thus users\n",
+ " | should start with these. Numerical checks require at least a partial solution to the\n",
+ " | model and should only be run once all structural issues have been resolved.\n",
+ " | \n",
+ " | Report methods will print a summary containing three parts:\n",
+ " | \n",
+ " | 1. Warnings - these are critical issues that should be resolved before continuing.\n",
+ " | For each warning, a method will be suggested in the Next Steps section to get\n",
+ " | additional information.\n",
+ " | 2. Cautions - these are things that could be correct but could also be the source of\n",
+ " | solver issues. Not all cautions need to be addressed, but users should investigate\n",
+ " | each one to ensure that the behavior is correct and that they will not be the source\n",
+ " | of difficulties later. Methods exist to provide more information on all cautions,\n",
+ " | but these will not appear in the Next Steps section.\n",
+ " | 3. Next Steps - these are recommended methods to call from the DiagnosticsToolbox to\n",
+ " | get further information on warnings. If no warnings are found, this will suggest\n",
+ " | the next report method to call.\n",
+ " | \n",
+ " | Args:\n",
+ " | \n",
+ " | model: model to be diagnosed. The DiagnosticsToolbox does not support indexed Blocks.\n",
+ " | \n",
+ " | Keyword Arguments\n",
+ " | -----------------\n",
+ " | variable_bounds_absolute_tolerance: float, default=0.0001\n",
+ " | Absolute tolerance for considering a variable to be close to its\n",
+ " | bounds.\n",
+ " | \n",
+ " | variable_bounds_relative_tolerance: float, default=0.0001\n",
+ " | Relative tolerance for considering a variable to be close to its\n",
+ " | bounds.\n",
+ " | \n",
+ " | variable_bounds_violation_tolerance: float, default=0\n",
+ " | Absolute tolerance for considering a variable to violate its bounds.\n",
+ " | Some solvers relax bounds on variables thus allowing a small violation\n",
+ " | to be considered acceptable.\n",
+ " | \n",
+ " | constraint_residual_tolerance: float, default=1e-05\n",
+ " | Absolute tolerance to use when checking constraint residuals.\n",
+ " | \n",
+ " | variable_large_value_tolerance: float, default=10000.0\n",
+ " | Absolute tolerance for considering a value to be large.\n",
+ " | \n",
+ " | variable_small_value_tolerance: float, default=0.0001\n",
+ " | Absolute tolerance for considering a value to be small.\n",
+ " | \n",
+ " | variable_zero_value_tolerance: float, default=1e-08\n",
+ " | Absolute tolerance for considering a value to be near to zero.\n",
+ " | \n",
+ " | jacobian_large_value_caution: float, default=10000.0\n",
+ " | Tolerance for raising a caution for large Jacobian values.\n",
+ " | \n",
+ " | jacobian_large_value_warning: float, default=100000000.0\n",
+ " | Tolerance for raising a warning for large Jacobian values.\n",
+ " | \n",
+ " | jacobian_small_value_caution: float, default=0.0001\n",
+ " | Tolerance for raising a caution for small Jacobian values.\n",
+ " | \n",
+ " | jacobian_small_value_warning: float, default=1e-08\n",
+ " | Tolerance for raising a warning for small Jacobian values.\n",
+ " | \n",
+ " | warn_for_evaluation_error_at_bounds: bool, default=True\n",
+ " | If False, warnings will not be generated for things like log(x) with x\n",
+ " | >= 0\n",
+ " | \n",
+ " | Methods defined here:\n",
+ " | \n",
+ " | __init__(self, model: pyomo.core.base.block._BlockData, **kwargs)\n",
+ " | Initialize self. See help(type(self)) for accurate signature.\n",
+ " | \n",
+ " | assert_no_numerical_warnings(self)\n",
+ " | Checks for numerical warnings in the model and raises an AssertionError\n",
+ " | if any are found.\n",
+ " | \n",
+ " | Raises:\n",
+ " | AssertionError if any warnings are identified by numerical analysis.\n",
+ " | \n",
+ " | assert_no_structural_warnings(self)\n",
+ " | Checks for structural warnings in the model and raises an AssertionError\n",
+ " | if any are found.\n",
+ " | \n",
+ " | Raises:\n",
+ " | AssertionError if any warnings are identified by structural analysis.\n",
+ " | \n",
+ " | display_components_with_inconsistent_units(self, stream=None)\n",
+ " | Prints a list of all Constraints, Expressions and Objectives in the\n",
+ " | model with inconsistent units of measurement.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_constraints_with_extreme_jacobians(self, stream=None)\n",
+ " | Prints the constraints associated with rows in the Jacobian with extreme\n",
+ " | L2 norms. This often indicates poorly scaled constraints.\n",
+ " | \n",
+ " | Tolerances can be set via the DiagnosticsToolbox config.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the output to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_constraints_with_large_residuals(self, stream=None)\n",
+ " | Prints a list of Constraints with residuals greater than a specified tolerance.\n",
+ " | Tolerance can be set in the class configuration options.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_external_variables(self, stream=None)\n",
+ " | Prints a list of variables that appear within activated Constraints in the\n",
+ " | model but are not contained within the model themselves.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_extreme_jacobian_entries(self, stream=None)\n",
+ " | Prints variables and constraints associated with entries in the Jacobian with extreme\n",
+ " | values. This can be indicative of poor scaling, especially for isolated terms (e.g.\n",
+ " | variables which appear only in one term of a single constraint).\n",
+ " | \n",
+ " | Tolerances can be set via the DiagnosticsToolbox config.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the output to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_overconstrained_set(self, stream=None)\n",
+ " | Prints the variables and constraints in the over-constrained sub-problem\n",
+ " | from a Dulmage-Mendelsohn partitioning.\n",
+ " | \n",
+ " | This can be used to identify the over-defined part of a model and thus\n",
+ " | where constraints must be removed or variables unfixed.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_potential_evaluation_errors(self, stream=None)\n",
+ " | Prints constraints that may be prone to evaluation errors\n",
+ " | (e.g., log of a negative number) based on variable bounds.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the output to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_underconstrained_set(self, stream=None)\n",
+ " | Prints the variables and constraints in the under-constrained sub-problem\n",
+ " | from a Dulmage-Mendelsohn partitioning.\n",
+ " | \n",
+ " | This can be used to identify the under-defined part of a model and thus\n",
+ " | where additional information (fixed variables or constraints) are required.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_unused_variables(self, stream=None)\n",
+ " | Prints a list of variables that do not appear in any activated Constraints.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_variables_at_or_outside_bounds(self, stream=None)\n",
+ " | Prints a list of variables with values that fall at or outside the bounds\n",
+ " | on the variable.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_variables_fixed_to_zero(self, stream=None)\n",
+ " | Prints a list of variables that are fixed to an absolute value of 0.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_variables_near_bounds(self, stream=None)\n",
+ " | Prints a list of variables with values close to their bounds. Tolerance can\n",
+ " | be set in the class configuration options.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_variables_with_extreme_jacobians(self, stream=None)\n",
+ " | Prints the variables associated with columns in the Jacobian with extreme\n",
+ " | L2 norms. This often indicates poorly scaled variables.\n",
+ " | \n",
+ " | Tolerances can be set via the DiagnosticsToolbox config.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the output to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_variables_with_extreme_values(self, stream=None)\n",
+ " | Prints a list of variables with extreme values.\n",
+ " | \n",
+ " | Tolerances can be set in the class configuration options.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_variables_with_none_value(self, stream=None)\n",
+ " | Prints a list of variables with a value of None.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_variables_with_value_near_zero(self, stream=None)\n",
+ " | Prints a list of variables with a value close to zero. The tolerance\n",
+ " | for determining what is close to zero can be set in the class configuration\n",
+ " | options.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | get_dulmage_mendelsohn_partition(self)\n",
+ " | Performs a Dulmage-Mendelsohn partitioning on the model and returns\n",
+ " | the over- and under-constrained sub-problems.\n",
+ " | \n",
+ " | Returns:\n",
+ " | list-of-lists variables in each independent block of the under-constrained set\n",
+ " | list-of-lists constraints in each independent block of the under-constrained set\n",
+ " | list-of-lists variables in each independent block of the over-constrained set\n",
+ " | list-of-lists constraints in each independent block of the over-constrained set\n",
+ " | \n",
+ " | prepare_degeneracy_hunter(self, **kwargs)\n",
+ " | Create an instance of the DegeneracyHunter and store as self.degeneracy_hunter.\n",
+ " | \n",
+ " | After creating an instance of the toolbox, call\n",
+ " | report_irreducible_degenerate_sets.\n",
+ " | \n",
+ " | Returns:\n",
+ " | \n",
+ " | Instance of DegeneracyHunter\n",
+ " | \n",
+ " | Keyword Arguments\n",
+ " | -----------------\n",
+ " | solver: str, default='scip'\n",
+ " | MILP solver to use for finding irreducible degenerate sets.\n",
+ " | \n",
+ " | solver_options: optional\n",
+ " | Options to pass to MILP solver.\n",
+ " | \n",
+ " | M: float, default=100000.0\n",
+ " | Maximum value for nu in MILP models.\n",
+ " | \n",
+ " | m_small: float, default=1e-05\n",
+ " | Smallest value for nu to be considered non-zero in MILP models.\n",
+ " | \n",
+ " | trivial_constraint_tolerance: float, default=1e-06\n",
+ " | Tolerance for identifying non-zero rows in Jacobian.\n",
+ " | \n",
+ " | prepare_svd_toolbox(self, **kwargs)\n",
+ " | Create an instance of the SVDToolbox and store as self.svd_toolbox.\n",
+ " | \n",
+ " | After creating an instance of the toolbox, call\n",
+ " | display_underdetermined_variables_and_constraints().\n",
+ " | \n",
+ " | Returns:\n",
+ " | \n",
+ " | Instance of SVDToolbox\n",
+ " | \n",
+ " | Keyword Arguments\n",
+ " | -----------------\n",
+ " | number_of_smallest_singular_values: PositiveInt, optional\n",
+ " | Number of smallest singular values to compute\n",
+ " | \n",
+ " | svd_callback: svd_callback_validator, default=\n",
+ " | Callback to SVD method of choice (default = svd_dense). Callbacks\n",
+ " | should take the Jacobian and number of singular values to compute as\n",
+ " | options, plus any method specific arguments, and should return the u,\n",
+ " | s and v matrices as numpy arrays.\n",
+ " | \n",
+ " | svd_callback_arguments: dict, optional\n",
+ " | Optional arguments to pass to SVD callback (default = None)\n",
+ " | \n",
+ " | singular_value_tolerance: float, default=1e-06\n",
+ " | Tolerance for defining a small singular value\n",
+ " | \n",
+ " | size_cutoff_in_singular_vector: float, default=0.1\n",
+ " | Size below which to ignore constraints and variables in the singular\n",
+ " | vector\n",
+ " | \n",
+ " | report_numerical_issues(self, stream=None)\n",
+ " | Generates a summary report of any numerical issues identified in the model provided\n",
+ " | and suggest next steps for debugging model.\n",
+ " | \n",
+ " | Numerical checks should only be performed once all structural issues have been resolved,\n",
+ " | and require that at least a partial solution to the model is available.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: I/O object to write report to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | report_structural_issues(self, stream=None)\n",
+ " | Generates a summary report of any structural issues identified in the model provided\n",
+ " | and suggests next steps for debugging the model.\n",
+ " | \n",
+ " | This should be the first method called when debugging a model and after any change\n",
+ " | is made to the model. These checks can be run before trying to initialize and solve\n",
+ " | the model.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: I/O object to write report to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | ----------------------------------------------------------------------\n",
+ " | Readonly properties defined here:\n",
+ " | \n",
+ " | model\n",
+ " | Model currently being diagnosed.\n",
+ " | \n",
+ " | ----------------------------------------------------------------------\n",
+ " | Data descriptors defined here:\n",
+ " | \n",
+ " | __dict__\n",
+ " | dictionary for instance variables (if defined)\n",
+ " | \n",
+ " | __weakref__\n",
+ " | list of weak references to the object (if defined)\n",
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "help(DiagnosticsToolbox)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The ``help()`` function gives us a lot of information on the ``DiagnosticsToolbox`` and all the methods that it supports (and there are many). However, the important part to start with are the four steps outlined at the top of the doc string that tell us how to get started.\n",
+ "\n",
+ "Firstly, we need a model to test (and, for this tutorial at least, one that has a wide range of issues that we need to fix before it will solve). We then also need to fix some variables so that we have 0 degrees of freedom in our model. Whilst our ultimate goal is generally optimization (and thus a system with 1 or more degrees of freedom), all models conceptually derive from a square model representing a nominal state. If this nominal state is not well-posed, then any issues present will also be present in the resulting optimization (even if adding degrees of freedom means that the model is now easier to solve).\n",
+ "\n",
+ "The cell below contains a demonstration model for this tutorial that contains a number of issues that we will resolve using the ``DiagnosticsToolbox``."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import pyomo.environ as pyo\n",
+ "\n",
+ "m = pyo.ConcreteModel()\n",
+ "\n",
+ "m.v1 = pyo.Var(units=pyo.units.m)\n",
+ "m.v2 = pyo.Var(units=pyo.units.m)\n",
+ "m.v3 = pyo.Var(bounds=(0, 5))\n",
+ "m.v4 = pyo.Var()\n",
+ "m.v5 = pyo.Var(bounds=(0, 10))\n",
+ "m.v6 = pyo.Var()\n",
+ "m.v7 = pyo.Var(\n",
+ " units=pyo.units.m, bounds=(0, 1)\n",
+ ") # Poorly scaled variable with lower bound\n",
+ "m.v8 = pyo.Var() # unused variable\n",
+ "\n",
+ "m.c1 = pyo.Constraint(expr=m.v1 + m.v2 == 10) # Unit consistency issue\n",
+ "m.c2 = pyo.Constraint(expr=m.v3 == m.v4 + m.v5)\n",
+ "m.c3 = pyo.Constraint(expr=2 * m.v3 == 3 * m.v4 + 4 * m.v5 + m.v6)\n",
+ "m.c4 = pyo.Constraint(expr=m.v7 == 1e-8 * m.v1) # Poorly scaled constraint\n",
+ "\n",
+ "m.v4.fix(2)\n",
+ "m.v5.fix(2)\n",
+ "m.v6.fix(0)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Next, the instructions tell us to create an instance of the ``DiagnosticsToolbox`` and to pass the model we wish to examine as an argument.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Create an instance of the DiagnosticsToolbox: dt = DiagnosticsToolbox(m)\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "dt = DiagnosticsToolbox(m)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Finally, the instructions tell us to run the ``report_structural_issues()`` method. Structural issues represent issues that exist solely in the form of the model equations and thus do not depend on the current value of any of the variables. This is useful as it means we can check for these before we even call a solver, which can be critical as sometimes these issues will cause a solver to fail without providing a useful solution.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Call dt.report_structural_issues() in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The toolbox is telling us that ``v3`` is the variable with a potential issue. It is also showing us the current value and bounds for ``v3`` as well as if it is a fixed or free variable, which will be useful for diagnosing the issues.\n",
- "\n",
- "We can see that ``v3`` is a free variable with bounds between 0 and 5 and a current value of 0. As ``v3`` is a free variable, this suggests that the solver has pushed the value to the bound where it cannot go any further, and this might be part of the cause of our infeasibility.\n",
- "\n",
- "\n",
- "Warning:\n",
- "When dealing with bounds violations you should always start by understanding why the bounds exist and what they mean - in many cases a bound indicates the range over which the model can be trusted and that going beyond this may result in unexpected behavior due to extrapolation.\n",
- " \n",
- "Never arbitrarily change a bound just because it is causing your model to be infeasible without understanding the consequences of this decision. Often, a bound violation is an indication that you need to re-think some of the constraints in your model to find alternatives which are valid in the actual range of values you are trying to solve for.\n",
- "
\n",
- "\n",
- "For this example, let us assume that we made a mistake with the bounds on ``v3`` and set the lower bound to be -5.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Update the bounds on v3 in the cell below.\n",
- "
"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Activated Blocks: 1 (Deactivated: 0)\n",
+ " Free Variables in Activated Constraints: 4 (External: 0)\n",
+ " Free Variables with only lower bounds: 0\n",
+ " Free Variables with only upper bounds: 0\n",
+ " Free Variables with upper and lower bounds: 2\n",
+ " Fixed Variables in Activated Constraints: 3 (External: 0)\n",
+ " Activated Equality Constraints: 4 (Deactivated: 0)\n",
+ " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
+ " Activated Objectives: 0 (Deactivated: 0)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 WARNINGS\n",
+ "\n",
+ " WARNING: 1 Component with inconsistent units\n",
+ " WARNING: Structural singularity found\n",
+ " Under-Constrained Set: 3 variables, 2 constraints\n",
+ " Over-Constrained Set: 1 variables, 2 constraints\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 Cautions\n",
+ "\n",
+ " Caution: 1 variable fixed to 0\n",
+ " Caution: 1 unused variable (0 fixed)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " display_components_with_inconsistent_units()\n",
+ " display_underconstrained_set()\n",
+ " display_overconstrained_set()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Looking at the output from the ``report_structural_issues()`` method, we can see that it provides a fairly short summary containing 4 sections.\n",
+ "\n",
+ "1. The first section is a summary of the size of the model, indicating things like the number of variables and constraints. The size of the model is often important for judging how difficult it will be to solve, and this information can also be useful for comparison to what is being sent to the solver. Most solvers will report the size of the model in their output logs, and if there is a difference between what is reported here and by the solver, then you should probably look into what is happening. This section also notes some things such as if you have any deactivated Blocks, Constraints or Objectives, or if you have variables which appear in the constraints that are not part of the model; these are not necessarily wrong but it is easy to have accidentally deactivated something you did not intend to so you should always check to see that these are expected.\n",
+ "\n",
+ "2. The second section provides a summary of any critical structural issues that were found - in this case we can see that there are 2 warnings we are going to need to look into. Warnings represent issues that need to be addressed before moving on as these will likely cause the solver to fail or give an incorrect answer.\n",
+ "\n",
+ "3. The third section lists a summary of any cautions that are found. Cautions represent issues that may or may not be problematic; in many cases these might be expected behaviors or borderline issues. However, these could also represent conceptual issues that should be addressed, so users should take the time to investigate these and determine if they need to be fixed or not.\n",
+ "\n",
+ "4. Finally, there is a section that suggests the next steps to take to help guide you through the model diagnosis process. If any warnings were identified, this section will list methods that can help you get more information on each specific problem, and if no warnings are found then it will guide you onto the next step in the model diagnosis workflow.\n",
+ "\n",
+ "**Note:** there are methods available to help investigate cautions as well, but these will not show up in the next steps in order to avoid cluttering the output. You can get more information on the available methods for investigating cautions via the documentation or ``help()`` function.\n",
+ "\n",
+ "In our current model, we have 2 critical issues (warnings) that we need to look into and resolve. The order in which we resolve these will generally not matter, but be aware that these can often be interrelated - fixing one warning might resolve other warnings as well (or create new ones), and sometimes you will need to look at multiple issues together to find the overall root cause.\n",
+ "\n",
+ "To start with, let us look at the unit consistency issue. From the \"Next Steps\" section above, the toolbox is suggesting we run the ``display_components_with_inconsistent_units()`` method for more information.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Call the `display_components_with_inconsistent_units()` method from the DiagnosticsToolbox to see more information on which constraint is causing the unit consistency issues.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {
+ "scrolled": false,
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "code",
- "execution_count": 32,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "m.v3.setlb(-5)"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "The following component(s) have unit consistency issues:\n",
+ "\n",
+ " c1\n",
+ "\n",
+ "For more details on unit inconsistencies, import the assert_units_consistent method\n",
+ "from pyomo.util.check_units\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.display_components_with_inconsistent_units()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This tells us that the issue lies in constraint ``c1``. If we go back and look at this constraint, we can see that it says ``v1 + v2 == 10``. ``v1`` and ``v2`` both have units of ``m`` which is consistent, but the constant in the expression (right hand side) is unitless. Thus, we need to correct this so that the right hand side has units for the constraint to be consistent.\n",
+ "\n",
+ "The cell below shows how to delete a constraint and replace it with a new one with the correct units.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "Deleting components can cause unexpected issues if something else in a model is using that component (e.g., deleting a variable which is used in a constraint). You should always be careful when deleting Pyomo components and make sure you only delete components that are not used elsewhere.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Delete the incorrect Constraint\n",
+ "m.del_component(m.c1)\n",
+ "\n",
+ "# Re-create the Constraint with the correct units\n",
+ "m.c1 = pyo.Constraint(expr=m.v1 + m.v2 == 10 * pyo.units.m)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Warning:\n",
+ "Fixing issues in models is often an iterative process requiring trial and error. You might also have some results from a model before running the diagnostics tools and the changes you make during debugging may make it difficult to replicate those results afterwards.\n",
+ " \n",
+ "It is strongly recommended that you keep a record of the changes you make at each step and why, along with a Git hash (or similar version control marker) corresponding to these changes. This will allow you see what changes and why, and give you a way to go back to previous iterations if the current approach does not work out. The IDAES documentation contains recommendations on how to keep and maintain a modeling logbook.\n",
+ "
\n",
+ "\n",
+ "Now, re-run the ``report_structural_issues()`` method and see if this change has fixed the unit consistency issue.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Call dt.report_structural_issues() in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {
+ "scrolled": true,
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Now that we have fixed the bounds issues, we should check whether our model is now feasible. However, before we continue we should recognize that we have just made a structural change to the model. If we were not careful, this could have introduced new structural issues to the model, so we should start from the beginning just to be sure.\n",
- "\n",
- "\n",
- "Warning:\n",
- "In general, you should always start from the beginning of the model diagnosis workflow after you make any change to the model. Remember to also record these changes in your log book in case something unexpected happens so that you can revert any changes that cause problems.\n",
- "
\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Check to see if there are any new structural issues in the cell below.\n",
- "
"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Activated Blocks: 1 (Deactivated: 0)\n",
+ " Free Variables in Activated Constraints: 4 (External: 0)\n",
+ " Free Variables with only lower bounds: 0\n",
+ " Free Variables with only upper bounds: 0\n",
+ " Free Variables with upper and lower bounds: 2\n",
+ " Fixed Variables in Activated Constraints: 3 (External: 0)\n",
+ " Activated Equality Constraints: 4 (Deactivated: 0)\n",
+ " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
+ " Activated Objectives: 0 (Deactivated: 0)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "1 WARNINGS\n",
+ "\n",
+ " WARNING: Structural singularity found\n",
+ " Under-Constrained Set: 3 variables, 2 constraints\n",
+ " Over-Constrained Set: 1 variables, 2 constraints\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 Cautions\n",
+ "\n",
+ " Caution: 1 variable fixed to 0\n",
+ " Caution: 1 unused variable (0 fixed)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " display_underconstrained_set()\n",
+ " display_overconstrained_set()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The unit consistency issue has been resolved by the changes above, so now we need to look at the structural singularity. A structural singularity occurs when one sub-part of the model is over-constrained (negative degrees of freedom), which generally means another part is under-constrained (positive degrees of freedom, assuming that there are 0 degrees of freedom overall).\n",
+ "\n",
+ "The toolbox is suggesting we use the ``display_overconstrained_set()`` and ``display_underconstrained_set()`` methods to get more information on the singularity; for now, let us start with the over-constrained set.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Call dt.display_overconstrained_set() in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "code",
- "execution_count": 34,
- "metadata": {
- "scrolled": true,
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Model Statistics\n",
- "\n",
- " Activated Blocks: 1 (Deactivated: 0)\n",
- " Free Variables in Activated Constraints: 4 (External: 0)\n",
- " Free Variables with only lower bounds: 0\n",
- " Free Variables with only upper bounds: 0\n",
- " Free Variables with upper and lower bounds: 2\n",
- " Fixed Variables in Activated Constraints: 3 (External: 0)\n",
- " Activated Equality Constraints: 4 (Deactivated: 0)\n",
- " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
- " Activated Objectives: 0 (Deactivated: 0)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "0 WARNINGS\n",
- "\n",
- " No warnings found!\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "2 Cautions\n",
- "\n",
- " Caution: 1 variable fixed to 0\n",
- " Caution: 1 unused variable (0 fixed)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "Suggested next steps:\n",
- "\n",
- " Try to initialize/solve your model and then call report_numerical_issues()\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.report_structural_issues()"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Dulmage-Mendelsohn Over-Constrained Set\n",
+ "\n",
+ " Independent Block 0:\n",
+ "\n",
+ " Variables:\n",
+ "\n",
+ " v3\n",
+ "\n",
+ " Constraints:\n",
+ "\n",
+ " c2\n",
+ " c3\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.display_overconstrained_set()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "From the output above, the toolbox is telling us that we have two constraints (``c2`` and ``c3``) which only contain a single unfixed variable (``v3``); thus in this part of the model we have -1 degree of freedom and the model is not well defined (structurally singular). If we go back and look at these constraints, we can see the that the constraints are:\n",
+ "\n",
+ "``c2: v3 == v4 + v5``\n",
+ "\n",
+ "``c3: 2*v3 == 3*v4 + 4*v5 + v6``\n",
+ "\n",
+ "We can see that in addition to ``v3`` these constraints actually contain 3 other variables (``v4``, ``v5`` and ``v6``), however these are all variables we fixed to get our initial zero degrees of freedom. It looks like we have either accidentally fixed one too many variables or written one too many constraints.\n",
+ "\n",
+ "For this example, let us assume that ``v4`` was not supposed to be fixed and unfix it.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Resolve the structural singularity and then call dt.report_structural_issues() in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "metadata": {
+ "scrolled": true,
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Our change has not introduced any new structural issues, so we can move on and try to solve the model again.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Re-solve the model in the cell below.\n",
- "
"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Activated Blocks: 1 (Deactivated: 0)\n",
+ " Free Variables in Activated Constraints: 5 (External: 0)\n",
+ " Free Variables with only lower bounds: 0\n",
+ " Free Variables with only upper bounds: 0\n",
+ " Free Variables with upper and lower bounds: 2\n",
+ " Fixed Variables in Activated Constraints: 2 (External: 0)\n",
+ " Activated Equality Constraints: 4 (Deactivated: 0)\n",
+ " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
+ " Activated Objectives: 0 (Deactivated: 0)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 WARNINGS\n",
+ "\n",
+ " WARNING: 1 Degree of Freedom\n",
+ " WARNING: Structural singularity found\n",
+ " Under-Constrained Set: 3 variables, 2 constraints\n",
+ " Over-Constrained Set: 0 variables, 0 constraints\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 Cautions\n",
+ "\n",
+ " Caution: 1 variable fixed to 0\n",
+ " Caution: 1 unused variable (0 fixed)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " display_underconstrained_set()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "m.v4.unfix()\n",
+ "\n",
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We can see that the over-constrained set is now empty (0 variables and 0 constraints) but the under-constrained set still has 3 variables and only 2 constraints. We can also see that there is a new warning about having 1 degree of freedom in the model, however this should not be surprising as we have just unfixed ``v4`` to resolve the over-constrained set so we have added a degree of freedom to the model.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Display the under-constrained set in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 20,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "code",
- "execution_count": 36,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "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...: 7\n",
- "Number of nonzeros in inequality constraint Jacobian.: 0\n",
- "Number of nonzeros in Lagrangian Hessian.............: 0\n",
- "\n",
- "Total number of variables............................: 4\n",
- " variables with only lower bounds: 0\n",
- " variables with lower and upper bounds: 2\n",
- " variables with only upper bounds: 0\n",
- "Total number of equality constraints.................: 4\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 6.67e-01 0.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
- " 1 0.0000000e+00 6.66e-03 2.97e+00 -1.0 2.00e+00 - 7.17e-01 9.90e-01h 1\n",
- " 2 0.0000000e+00 6.27e-05 9.38e+00 -1.0 2.00e-02 - 1.00e+00 9.91e-01h 1\n",
- " 3 0.0000000e+00 8.88e-16 1.13e-12 -1.0 1.88e-04 - 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....: 8.8817841970012523e-16 8.8817841970012523e-16\n",
- "Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
- "Overall NLP error.......: 8.8817841970012523e-16 8.8817841970012523e-16\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"
- ]
- },
- {
- "data": {
- "text/plain": [
- "{'Problem': [{'Lower bound': -inf, 'Upper bound': inf, 'Number of objectives': 1, 'Number of constraints': 4, 'Number of variables': 4, 'Sense': 'unknown'}], 'Solver': [{'Status': 'ok', 'Message': 'Ipopt 3.13.2\\\\x3a Optimal Solution Found', 'Termination condition': 'optimal', 'Id': 0, 'Error rc': 0, 'Time': 0.02317023277282715}], 'Solution': [OrderedDict([('number of solutions', 0), ('number of solutions displayed', 0)])]}"
- ]
- },
- "execution_count": 36,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "solver.solve(m, tee=True)"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Dulmage-Mendelsohn Under-Constrained Set\n",
+ "\n",
+ " Independent Block 0:\n",
+ "\n",
+ " Variables:\n",
+ "\n",
+ " v2\n",
+ " v1\n",
+ " v7\n",
+ "\n",
+ " Constraints:\n",
+ "\n",
+ " c1\n",
+ " c4\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.display_underconstrained_set()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Looking at the output from the ``display_underconstrained_set()`` method, we can see that we have two constraints, ``c1`` and ``c4``, which contain three unfixed variables, ``v1``, ``v2`` and ``v7``. Thus, we have one degree of freedom that needs to be addressed. To fix this, we could either fix one of the variables shown or add an additional equality constraint to the model.\n",
+ "\n",
+ "For this example let's fix ``v2`` to a value of 5 and then re-run the ``report_structural_issues()`` method.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Fix v2 to a value of 5 and then re-run dt.report_structural_issues.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "metadata": {
+ "scrolled": true,
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "IPOPT should have returned optimal solution now, so it looks like those bounds were what was causing the model to be infeasible. At this point, the model is now solving (for the current values at least), so you might think that the model is now ready for optimization.\n",
- "\n",
- "However, if we look at the solver logs we can see that it took around 3 iterations for IPOPT to solve our model (depending on minor variations in computer architecture). For a model this simple, we would generally expect it to solve in only 1 iteration so there is still some room for improvement.\n",
- "\n",
- "\n",
- "Warning:\n",
- "You should keep in mind that just because you get an optimal solution does not mean that your model is robust and free of issues.\n",
- " \n",
- "You should always take the time to look over the solver logs to look for signs of trouble, even if you get an optimal solution. While you might get an optimal solution for the current state, there may be advance warning signs of issues that will cause problems later when you try to solve the model at a different state.\n",
- "
\n",
- "\n",
- "Let us run the ``report_numerical_issues`` method again to see if there are any other problems we need to address.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Check for additional numerical issues in the cell below.\n",
- "
"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Activated Blocks: 1 (Deactivated: 0)\n",
+ " Free Variables in Activated Constraints: 4 (External: 0)\n",
+ " Free Variables with only lower bounds: 0\n",
+ " Free Variables with only upper bounds: 0\n",
+ " Free Variables with upper and lower bounds: 2\n",
+ " Fixed Variables in Activated Constraints: 3 (External: 0)\n",
+ " Activated Equality Constraints: 4 (Deactivated: 0)\n",
+ " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
+ " Activated Objectives: 0 (Deactivated: 0)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "0 WARNINGS\n",
+ "\n",
+ " No warnings found!\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 Cautions\n",
+ "\n",
+ " Caution: 1 variable fixed to 0\n",
+ " Caution: 1 unused variable (0 fixed)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " Try to initialize/solve your model and then call report_numerical_issues()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "m.v2.fix(5)\n",
+ "\n",
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The toolbox is now telling us that no warnings were found, so we have resolved all the structural issues (for now at least). The toolbox is telling us that there are also 2 non-critical issues (cautions) that we should look at; one about an unused variable and one about a variable fixed to zero. If you wish, you can look into identifying and fixing these yourself, however for this example we will move on to the next step (remember that the toolbox has methods to display more details for each of these which you can find in the documentation or from the ``help()`` function).\n",
+ "\n",
+ "For the Next Steps section, the toolbox is recommending we try to solve our model and then check for numerical issues.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Use the Pyomo SolverFactory to create an instance of IPOPT and then try to solve the model. Make sure to set \"tee=True\" as this is going to fail (and it is always good practice to review the solver logs).\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "code",
- "execution_count": 38,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Model Statistics\n",
- "\n",
- " Jacobian Condition Number: 1.700E+01\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "0 WARNINGS\n",
- "\n",
- " No warnings found!\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "5 Cautions\n",
- "\n",
- " Caution: 1 Variable with value close to their bounds (abs=1.0E-04, rel=1.0E-04)\n",
- " Caution: 1 Variable with value close to zero (tol=1.0E-08)\n",
- " Caution: 1 Variable with extreme value (<1.0E-04 or >1.0E+04)\n",
- " Caution: 1 Variable with None value\n",
- " Caution: 1 extreme Jacobian Entry (<1.0E-04 or >1.0E+04)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "Suggested next steps:\n",
- "\n",
- " If you still have issues converging your model consider:\n",
- " prepare_svd_toolbox()\n",
- " prepare_degeneracy_hunter()\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.report_numerical_issues()"
- ]
+ "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...: 7\n",
+ "Number of nonzeros in inequality constraint Jacobian.: 0\n",
+ "Number of nonzeros in Lagrangian Hessian.............: 0\n",
+ "\n",
+ "Total number of variables............................: 4\n",
+ " variables with only lower bounds: 0\n",
+ " variables with lower and upper bounds: 2\n",
+ " variables with only upper bounds: 0\n",
+ "Total number of equality constraints.................: 4\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 1.40e+01 0.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
+ " 1 0.0000000e+00 1.39e+01 1.50e+02 -1.0 6.00e+00 - 7.16e-01 4.93e-03h 1\n",
+ " 2 0.0000000e+00 1.39e+01 3.03e+06 -1.0 5.97e+00 - 1.00e+00 4.95e-05h 1\n",
+ " 3r 0.0000000e+00 1.39e+01 1.00e+03 1.1 0.00e+00 - 0.00e+00 2.47e-07R 2\n",
+ " 4r 0.0000000e+00 4.19e+00 9.42e+02 1.1 3.50e+03 - 4.02e-01 3.37e-03f 1\n",
+ " 5r 0.0000000e+00 2.12e+00 8.72e+02 1.1 5.89e+01 - 4.35e-01 7.06e-02f 1\n",
+ " 6r 0.0000000e+00 6.74e-01 6.06e+02 1.1 5.29e+00 - 9.93e-03 3.98e-01f 1\n",
+ " 7r 0.0000000e+00 6.80e-01 3.14e+02 0.4 2.05e-01 - 1.00e+00 1.03e-01f 1\n",
+ " 8r 0.0000000e+00 6.69e-01 2.78e-05 0.4 2.58e-02 - 1.00e+00 1.00e+00f 1\n",
+ " 9r 0.0000000e+00 6.67e-01 7.56e+00 -1.7 8.13e-03 - 9.93e-01 9.96e-01f 1\n",
+ "iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n",
+ " 10r 0.0000000e+00 6.67e-01 2.23e-07 -1.7 4.13e-05 - 1.00e+00 1.00e+00f 1\n",
+ " 11r 0.0000000e+00 6.67e-01 6.73e-01 -3.7 6.61e-05 - 1.00e+00 1.00e+00f 1\n",
+ " 12r 0.0000000e+00 6.67e-01 1.91e-09 -3.7 1.48e-09 - 1.00e+00 1.00e+00h 1\n",
+ " 13r 0.0000000e+00 6.67e-01 2.69e+00 -8.4 5.74e-07 - 1.00e+00 9.26e-01f 1\n",
+ " 14r 0.0000000e+00 6.67e-01 7.65e+01 -8.4 4.23e-08 - 8.68e-01 1.00e+00f 1\n",
+ "\n",
+ "Number of Iterations....: 14\n",
+ "\n",
+ " (scaled) (unscaled)\n",
+ "Objective...............: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Dual infeasibility......: 3.2644919411246030e-04 3.2644919411246030e-04\n",
+ "Constraint violation....: 6.6666666333656233e-01 6.6666666333656233e-01\n",
+ "Complementarity.........: 4.6615546565561981e-09 4.6615546565561981e-09\n",
+ "Overall NLP error.......: 6.6666666333656233e-01 6.6666666333656233e-01\n",
+ "\n",
+ "\n",
+ "Number of objective function evaluations = 18\n",
+ "Number of objective gradient evaluations = 5\n",
+ "Number of equality constraint evaluations = 18\n",
+ "Number of inequality constraint evaluations = 0\n",
+ "Number of equality constraint Jacobian evaluations = 17\n",
+ "Number of inequality constraint Jacobian evaluations = 0\n",
+ "Number of Lagrangian Hessian evaluations = 15\n",
+ "Total CPU secs in IPOPT (w/o function evaluations) = 0.003\n",
+ "Total CPU secs in NLP function evaluations = 0.000\n",
+ "\n",
+ "EXIT: Converged to a point of local infeasibility. Problem may be infeasible.\n",
+ "WARNING: Loading a SolverResults object with a warning status into\n",
+ "model.name=\"unknown\";\n",
+ " - termination condition: infeasible\n",
+ " - message from solver: Ipopt 3.13.2\\x3a Converged to a locally infeasible\n",
+ " point. Problem may be infeasible.\n"
+ ]
},
{
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The toolbox is not reporting any warnings which is good, however there are still 5 numerical cautions that it has identified which might be contributing to the larger than expected number of iterations. As mentioned earlier, the toolbox does not suggest methods for investigating these, but there are methods available. For example, we can look at the variable with an extreme value using the `display_variables_with_extreme_values()` method.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Check for additional information about variables with extreme values.\n",
- "
"
+ "data": {
+ "text/plain": [
+ "{'Problem': [{'Lower bound': -inf, 'Upper bound': inf, 'Number of objectives': 1, 'Number of constraints': 4, 'Number of variables': 4, 'Sense': 'unknown'}], 'Solver': [{'Status': 'warning', 'Message': 'Ipopt 3.13.2\\\\x3a Converged to a locally infeasible point. Problem may be infeasible.', 'Termination condition': 'infeasible', 'Id': 200, 'Error rc': 0, 'Time': 0.007064104080200195}], 'Solution': [OrderedDict([('number of solutions', 0), ('number of solutions displayed', 0)])]}"
]
- },
+ },
+ "execution_count": 24,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "solver = pyo.SolverFactory(\"ipopt\")\n",
+ "solver.solve(m, tee=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As hinted at above, IPOPT has returned a warning that the problem may be infeasible. Before moving on however, it is always good practice to look over the solver outputs and see what it is telling you.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "A lot of useful information is contained in the solver logs which is extremely useful when diagnosing modeling issues. Each solver has its own way of reporting output and its own specific behavior, so you will need to learn to interpret the output of each solver you use. The IDAES Documentation contains some guidance on interpreting output logs for a few common solvers.\n",
+ "
\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Call the report_numerical_issues method in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 26,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "code",
- "execution_count": 40,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "The following variable(s) have extreme values (<1.0E-04 or > 1.0E+04):\n",
- "\n",
- " v7: 4.9999999999999945e-08\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.display_variables_with_extreme_values()"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Jacobian Condition Number: 1.700E+01\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 WARNINGS\n",
+ "\n",
+ " WARNING: 1 Constraint with large residuals (>1.0E-05)\n",
+ " WARNING: 1 Variable at or outside bounds (tol=0.0E+00)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "5 Cautions\n",
+ "\n",
+ " Caution: 2 Variables with value close to their bounds (abs=1.0E-04, rel=1.0E-04)\n",
+ " Caution: 2 Variables with value close to zero (tol=1.0E-08)\n",
+ " Caution: 1 Variable with extreme value (<1.0E-04 or >1.0E+04)\n",
+ " Caution: 1 Variable with None value\n",
+ " Caution: 1 extreme Jacobian Entry (<1.0E-04 or >1.0E+04)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " display_constraints_with_large_residuals()\n",
+ " display_variables_at_or_outside_bounds()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.report_numerical_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The ``report_numerical_issues()`` provides a summary similar to that which we saw for the structural issues. Firstly, it reports to us the Jacobian condition number for our problem which can give us an idea of how well-scaled the problem is, followed by a list of warnings, cautions and suggested next steps.\n",
+ "\n",
+ "Unsurprisingly, we are seeing a warning about a constraint with a large residual which we would expect when a solver reports a potentially infeasible problem. We are also seeing a warning about a variable with bound violations which might be contributing to the potential infeasibility.\n",
+ "\n",
+ "For the next steps, the toolbox is suggesting some new methods to get more information on these issues; let us start by looking at the constraints with large residuals.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Display the constraint with a large residual in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 28,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We can see that ``v7`` is potentially causing problems due to having a very small value (on the order of magnitude of the solver tolerance). This can be especially problematic for interior point solvers like IPOPT if there is a lower bound of 0 (which there is in this case). IPOPT tries to avoid bounds and thus perturbs solutions away from these if it gets too close, which can cause convergence to be slow (or fail) if the solution lies close to the bound.\n",
- "\n",
- "We can address this by scaling the variable so that the value of the scaled variable is large enough that the solution is not close to the lower bound. Additionally, we should look at any constraint that ``v7`` appears in (in this case ``c4``) and ensure that those constraints are well scaled as well (so that a residual of 1e-6 is reasonable for the terms involved).\n",
- "\n",
- "For this case, we can set a scaling factor of 1e8 for both ``v7`` and ``c4`` as shown below. Note that we also need to apply Pyomo's scaling transformation to create a new scaled model to work with."
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "The following constraint(s) have large residuals (>1.0E-05):\n",
+ "\n",
+ " c2: 6.66667E-01\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.display_constraints_with_large_residuals()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The toolbox is telling us that the constraint which failed to converge is ``c2``, however this is generally only part of the story. Solvers work by trying to minimize the infeasibility in the model (residual of the constraints), which generally means they push any infeasibility onto the least sensitive constraint in the problem. Thus, the constraint which shows the infeasibility is often not the root cause of the problem, but only the symptom of the underlying issue.\n",
+ "\n",
+ "If we look back at the constraints, we can see that the same variables also appear in ``c3`` and that some of these have bounds, all of which could be contributing to the infeasibility. In this case the solver tried to minimize the residual in all the constraints and ended up pushing all the issues off onto ``c2``.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "When dealing with solver issues such as this, you should always remember that the obvious symptoms are often just the tip of the iceberg and that the real issue generally lies somewhere else; the challenge is tracing the symptoms back to their ultimate source.\n",
+ "
\n",
+ "\n",
+ "Next, let us take a look at the variables at or outside their bounds as well. When a solver reports an potentially infeasible solution, the most common cause is unexpected bounds violations so you should always check these first.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Display the variables with bounds violations.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 30,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "code",
- "execution_count": 41,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.scaling_factor = pyo.Suffix(direction=pyo.Suffix.EXPORT)\n",
- "\n",
- "m.scaling_factor[m.v7] = 1e8\n",
- "m.scaling_factor[m.c4] = 1e8\n",
- "\n",
- "scaling = pyo.TransformationFactory('core.scale_model')\n",
- "scaled_model = scaling.create_using(m, rename=False)"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "The following variable(s) have values at or outside their bounds (tol=0.0E+00):\n",
+ "\n",
+ " v3 (free): value=0.0 bounds=(0, 5)\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.display_variables_at_or_outside_bounds()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The toolbox is telling us that ``v3`` is the variable with a potential issue. It is also showing us the current value and bounds for ``v3`` as well as if it is a fixed or free variable, which will be useful for diagnosing the issues.\n",
+ "\n",
+ "We can see that ``v3`` is a free variable with bounds between 0 and 5 and a current value of 0. As ``v3`` is a free variable, this suggests that the solver has pushed the value to the bound where it cannot go any further, and this might be part of the cause of our infeasibility.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "When dealing with bounds violations you should always start by understanding why the bounds exist and what they mean - in many cases a bound indicates the range over which the model can be trusted and that going beyond this may result in unexpected behavior due to extrapolation.\n",
+ " \n",
+ "Never arbitrarily change a bound just because it is causing your model to be infeasible without understanding the consequences of this decision. Often, a bound violation is an indication that you need to re-think some of the constraints in your model to find alternatives which are valid in the actual range of values you are trying to solve for.\n",
+ "
\n",
+ "\n",
+ "For this example, let us assume that we made a mistake with the bounds on ``v3`` and set the lower bound to be -5.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Update the bounds on v3 in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 32,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "m.v3.setlb(-5)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now that we have fixed the bounds issues, we should check whether our model is now feasible. However, before we continue we should recognize that we have just made a structural change to the model. If we were not careful, this could have introduced new structural issues to the model, so we should start from the beginning just to be sure.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "In general, you should always start from the beginning of the model diagnosis workflow after you make any change to the model. Remember to also record these changes in your log book in case something unexpected happens so that you can revert any changes that cause problems.\n",
+ "
\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Check to see if there are any new structural issues in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 34,
+ "metadata": {
+ "scrolled": true,
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Now that we have a scaled model, we can try to solve it and hopefully see better convergence than the unscaled model.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Solve the scaled model and check to see how many iterations are required.\n",
- "
"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Activated Blocks: 1 (Deactivated: 0)\n",
+ " Free Variables in Activated Constraints: 4 (External: 0)\n",
+ " Free Variables with only lower bounds: 0\n",
+ " Free Variables with only upper bounds: 0\n",
+ " Free Variables with upper and lower bounds: 2\n",
+ " Fixed Variables in Activated Constraints: 3 (External: 0)\n",
+ " Activated Equality Constraints: 4 (Deactivated: 0)\n",
+ " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
+ " Activated Objectives: 0 (Deactivated: 0)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "0 WARNINGS\n",
+ "\n",
+ " No warnings found!\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 Cautions\n",
+ "\n",
+ " Caution: 1 variable fixed to 0\n",
+ " Caution: 1 unused variable (0 fixed)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " Try to initialize/solve your model and then call report_numerical_issues()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Our change has not introduced any new structural issues, so we can move on and try to solve the model again.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Re-solve the model in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 36,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "code",
- "execution_count": 43,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "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...: 7\n",
- "Number of nonzeros in inequality constraint Jacobian.: 0\n",
- "Number of nonzeros in Lagrangian Hessian.............: 0\n",
- "\n",
- "Total number of variables............................: 4\n",
- " variables with only lower bounds: 0\n",
- " variables with lower and upper bounds: 2\n",
- " variables with only upper bounds: 0\n",
- "Total number of equality constraints.................: 4\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 5.33e-15 0.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
- "\n",
- "Number of Iterations....: 0\n",
- "\n",
- " (scaled) (unscaled)\n",
- "Objective...............: 0.0000000000000000e+00 0.0000000000000000e+00\n",
- "Dual infeasibility......: 0.0000000000000000e+00 0.0000000000000000e+00\n",
- "Constraint violation....: 5.3290705182007514e-15 5.3290705182007514e-15\n",
- "Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
- "Overall NLP error.......: 5.3290705182007514e-15 5.3290705182007514e-15\n",
- "\n",
- "\n",
- "Number of objective function evaluations = 1\n",
- "Number of objective gradient evaluations = 1\n",
- "Number of equality constraint evaluations = 1\n",
- "Number of inequality constraint evaluations = 0\n",
- "Number of equality constraint Jacobian evaluations = 1\n",
- "Number of inequality constraint Jacobian evaluations = 0\n",
- "Number of Lagrangian Hessian evaluations = 0\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"
- ]
- },
- {
- "data": {
- "text/plain": [
- "{'Problem': [{'Lower bound': -inf, 'Upper bound': inf, 'Number of objectives': 1, 'Number of constraints': 4, 'Number of variables': 4, 'Sense': 'unknown'}], 'Solver': [{'Status': 'ok', 'Message': 'Ipopt 3.13.2\\\\x3a Optimal Solution Found', 'Termination condition': 'optimal', 'Id': 0, 'Error rc': 0, 'Time': 0.0058002471923828125}], 'Solution': [OrderedDict([('number of solutions', 0), ('number of solutions displayed', 0)])]}"
- ]
- },
- "execution_count": 43,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "solver.solve(scaled_model, tee=True)"
- ]
+ "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...: 7\n",
+ "Number of nonzeros in inequality constraint Jacobian.: 0\n",
+ "Number of nonzeros in Lagrangian Hessian.............: 0\n",
+ "\n",
+ "Total number of variables............................: 4\n",
+ " variables with only lower bounds: 0\n",
+ " variables with lower and upper bounds: 2\n",
+ " variables with only upper bounds: 0\n",
+ "Total number of equality constraints.................: 4\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 6.67e-01 0.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
+ " 1 0.0000000e+00 6.66e-03 2.97e+00 -1.0 2.00e+00 - 7.17e-01 9.90e-01h 1\n",
+ " 2 0.0000000e+00 6.27e-05 9.38e+00 -1.0 2.00e-02 - 1.00e+00 9.91e-01h 1\n",
+ " 3 0.0000000e+00 8.88e-16 1.13e-12 -1.0 1.88e-04 - 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....: 8.8817841970012523e-16 8.8817841970012523e-16\n",
+ "Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Overall NLP error.......: 8.8817841970012523e-16 8.8817841970012523e-16\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"
+ ]
},
{
- "cell_type": "code",
- "execution_count": 44,
- "metadata": {
- "tags": [
- "testing"
- ]
- },
- "outputs": [],
- "source": [
- "from pyomo.environ import assert_optimal_termination\n",
- "res = solver.solve(scaled_model, tee=False)\n",
- "assert_optimal_termination(res)"
+ "data": {
+ "text/plain": [
+ "{'Problem': [{'Lower bound': -inf, 'Upper bound': inf, 'Number of objectives': 1, 'Number of constraints': 4, 'Number of variables': 4, 'Sense': 'unknown'}], 'Solver': [{'Status': 'ok', 'Message': 'Ipopt 3.13.2\\\\x3a Optimal Solution Found', 'Termination condition': 'optimal', 'Id': 0, 'Error rc': 0, 'Time': 0.02317023277282715}], 'Solution': [OrderedDict([('number of solutions', 0), ('number of solutions displayed', 0)])]}"
]
- },
+ },
+ "execution_count": 36,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "solver.solve(m, tee=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "IPOPT should have returned optimal solution now, so it looks like those bounds were what was causing the model to be infeasible. At this point, the model is now solving (for the current values at least), so you might think that the model is now ready for optimization.\n",
+ "\n",
+ "However, if we look at the solver logs we can see that it took around 3 iterations for IPOPT to solve our model (depending on minor variations in computer architecture). For a model this simple, we would generally expect it to solve in only 1 iteration so there is still some room for improvement.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "You should keep in mind that just because you get an optimal solution does not mean that your model is robust and free of issues.\n",
+ " \n",
+ "You should always take the time to look over the solver logs to look for signs of trouble, even if you get an optimal solution. While you might get an optimal solution for the current state, there may be advance warning signs of issues that will cause problems later when you try to solve the model at a different state.\n",
+ "
\n",
+ "\n",
+ "Let us run the ``report_numerical_issues`` method again to see if there are any other problems we need to address.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Check for additional numerical issues in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 38,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "As we can see, the scaled model solved in 0 iterations (indicating that it already had the right solution). However, had we done this to the unscaled model we would have found it required 2-3 iterations again due to IPOPT perturbing the initial (correct) solution away from the bounds.\n",
- "\n",
- "\n",
- "Warning:\n",
- "Normally in these cases we would need to map the solution from the scaled model back to the unscaled model so we can view the results. In this case, we are not actually interested in the solution so we move on with the model diagnosis.\n",
- "
\n",
- "\n",
- "Now that we have fixed the scaling issues, we can go back to the ``DiagnosticsToolbox`` and see if we still have any warnings. Note however that we need to look at the scaled model now rather than the original model, so we need to create a new instance of the ``DiagnositcsToolbox`` with the scaled model as the ``model`` argument.\n",
- "\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Create a new instance of the DiagnosticsToolbox and check the scaled model for issues.\n",
- "
"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Jacobian Condition Number: 1.700E+01\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "0 WARNINGS\n",
+ "\n",
+ " No warnings found!\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "5 Cautions\n",
+ "\n",
+ " Caution: 1 Variable with value close to their bounds (abs=1.0E-04, rel=1.0E-04)\n",
+ " Caution: 1 Variable with value close to zero (tol=1.0E-08)\n",
+ " Caution: 1 Variable with extreme value (<1.0E-04 or >1.0E+04)\n",
+ " Caution: 1 Variable with None value\n",
+ " Caution: 1 extreme Jacobian Entry (<1.0E-04 or >1.0E+04)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " If you still have issues converging your model consider:\n",
+ " prepare_svd_toolbox()\n",
+ " prepare_degeneracy_hunter()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.report_numerical_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The toolbox is not reporting any warnings which is good, however there are still 5 numerical cautions that it has identified which might be contributing to the larger than expected number of iterations. As mentioned earlier, the toolbox does not suggest methods for investigating these, but there are methods available. For example, we can look at the variable with an extreme value using the `display_variables_with_extreme_values()` method.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Check for additional information about variables with extreme values.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 40,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "code",
- "execution_count": 46,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Model Statistics\n",
- "\n",
- " Jacobian Condition Number: 1.800E+01\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "0 WARNINGS\n",
- "\n",
- " No warnings found!\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "3 Cautions\n",
- "\n",
- " Caution: 1 Variable with value close to their bounds (abs=1.0E-04, rel=1.0E-04)\n",
- " Caution: 1 Variable with value close to zero (tol=1.0E-08)\n",
- " Caution: 1 Variable with None value\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "Suggested next steps:\n",
- "\n",
- " If you still have issues converging your model consider:\n",
- " prepare_svd_toolbox()\n",
- " prepare_degeneracy_hunter()\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt_scaled = DiagnosticsToolbox(scaled_model)\n",
- "dt_scaled.report_numerical_issues()"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "The following variable(s) have extreme values (<1.0E-04 or > 1.0E+04):\n",
+ "\n",
+ " v7: 4.9999999999999945e-08\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.display_variables_with_extreme_values()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We can see that ``v7`` is potentially causing problems due to having a very small value (on the order of magnitude of the solver tolerance). This can be especially problematic for interior point solvers like IPOPT if there is a lower bound of 0 (which there is in this case). IPOPT tries to avoid bounds and thus perturbs solutions away from these if it gets too close, which can cause convergence to be slow (or fail) if the solution lies close to the bound.\n",
+ "\n",
+ "We can address this by scaling the variable so that the value of the scaled variable is large enough that the solution is not close to the lower bound. Additionally, we should look at any constraint that ``v7`` appears in (in this case ``c4``) and ensure that those constraints are well scaled as well (so that a residual of 1e-6 is reasonable for the terms involved).\n",
+ "\n",
+ "For this case, we can set a scaling factor of 1e8 for both ``v7`` and ``c4`` as shown below. Note that we also need to apply Pyomo's scaling transformation to create a new scaled model to work with."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 41,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.scaling_factor = pyo.Suffix(direction=pyo.Suffix.EXPORT)\n",
+ "\n",
+ "m.scaling_factor[m.v7] = 1e8\n",
+ "m.scaling_factor[m.c4] = 1e8\n",
+ "\n",
+ "scaling = pyo.TransformationFactory(\"core.scale_model\")\n",
+ "scaled_model = scaling.create_using(m, rename=False)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now that we have a scaled model, we can try to solve it and hopefully see better convergence than the unscaled model.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Solve the scaled model and check to see how many iterations are required.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 43,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
{
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We can see that applying scaling addressed two of the cautions we had before (the variable with an extreme value and an associated large value in the model Jacobian). Whilst we were able to solve the unscaled model in this case, this is in part because it was a simple linear model. In more complex, non-linear models, scaling becomes much more important and often depends strongly on the current state of the model. That is, you can often find cases where the unscaled (or poorly scaled) model solves for a limited range of conditions but fails to solve if you move too far away for the current state. Whilst you might be able to solve the model at the current state, you should always check the solver logs and numerical cautions for advanced warning signs of scaling issues that might manifest later when you try to solve the model for a different state (e.g., during optimization).\n",
- "\n",
- "\n",
- "Warning:\n",
- "By their nature, numerical issues depend on the current values of the variables in the model, and thus may remain hidden until someone tries to solve the model close to where the issue exists. For this reason, the full model diagnostics workflow contains steps to run the numerical checks across a wide range of variable values to try to ensure that no issues remain hidden. This is beyond the scope of this tutorial however.\n",
- "
\n",
- "\n",
- "At this point, we have addressed all the issues that were preventing us from solving the demonstration model and so reached the end of this tutorial. For cases where we are still having trouble solving the model, we can see that the toolbox is suggesting additional methods for further debugging and these advanced features will be the focus of separate tutorials."
- ]
+ "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...: 7\n",
+ "Number of nonzeros in inequality constraint Jacobian.: 0\n",
+ "Number of nonzeros in Lagrangian Hessian.............: 0\n",
+ "\n",
+ "Total number of variables............................: 4\n",
+ " variables with only lower bounds: 0\n",
+ " variables with lower and upper bounds: 2\n",
+ " variables with only upper bounds: 0\n",
+ "Total number of equality constraints.................: 4\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 5.33e-15 0.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
+ "\n",
+ "Number of Iterations....: 0\n",
+ "\n",
+ " (scaled) (unscaled)\n",
+ "Objective...............: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Dual infeasibility......: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Constraint violation....: 5.3290705182007514e-15 5.3290705182007514e-15\n",
+ "Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Overall NLP error.......: 5.3290705182007514e-15 5.3290705182007514e-15\n",
+ "\n",
+ "\n",
+ "Number of objective function evaluations = 1\n",
+ "Number of objective gradient evaluations = 1\n",
+ "Number of equality constraint evaluations = 1\n",
+ "Number of inequality constraint evaluations = 0\n",
+ "Number of equality constraint Jacobian evaluations = 1\n",
+ "Number of inequality constraint Jacobian evaluations = 0\n",
+ "Number of Lagrangian Hessian evaluations = 0\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"
+ ]
},
{
- "cell_type": "code",
- "execution_count": 47,
- "metadata": {
- "tags": [
- "testing"
- ]
- },
- "outputs": [],
- "source": [
- "dt.assert_no_structural_warnings()\n",
- "dt.assert_no_numerical_warnings()"
+ "data": {
+ "text/plain": [
+ "{'Problem': [{'Lower bound': -inf, 'Upper bound': inf, 'Number of objectives': 1, 'Number of constraints': 4, 'Number of variables': 4, 'Sense': 'unknown'}], 'Solver': [{'Status': 'ok', 'Message': 'Ipopt 3.13.2\\\\x3a Optimal Solution Found', 'Termination condition': 'optimal', 'Id': 0, 'Error rc': 0, 'Time': 0.0058002471923828125}], 'Solution': [OrderedDict([('number of solutions', 0), ('number of solutions displayed', 0)])]}"
]
+ },
+ "execution_count": 43,
+ "metadata": {},
+ "output_type": "execute_result"
}
- ],
- "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"
+ ],
+ "source": [
+ "solver.solve(scaled_model, tee=True)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 44,
+ "metadata": {
+ "tags": [
+ "testing"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "from pyomo.environ import assert_optimal_termination\n",
+ "\n",
+ "res = solver.solve(scaled_model, tee=False)\n",
+ "assert_optimal_termination(res)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As we can see, the scaled model solved in 0 iterations (indicating that it already had the right solution). However, had we done this to the unscaled model we would have found it required 2-3 iterations again due to IPOPT perturbing the initial (correct) solution away from the bounds.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "Normally in these cases we would need to map the solution from the scaled model back to the unscaled model so we can view the results. In this case, we are not actually interested in the solution so we move on with the model diagnosis.\n",
+ "
\n",
+ "\n",
+ "Now that we have fixed the scaling issues, we can go back to the ``DiagnosticsToolbox`` and see if we still have any warnings. Note however that we need to look at the scaled model now rather than the original model, so we need to create a new instance of the ``DiagnosticsToolbox`` with the scaled model as the ``model`` argument.\n",
+ "\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Create a new instance of the DiagnosticsToolbox and check the scaled model for issues.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 46,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Jacobian Condition Number: 1.800E+01\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "0 WARNINGS\n",
+ "\n",
+ " No warnings found!\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "3 Cautions\n",
+ "\n",
+ " Caution: 1 Variable with value close to their bounds (abs=1.0E-04, rel=1.0E-04)\n",
+ " Caution: 1 Variable with value close to zero (tol=1.0E-08)\n",
+ " Caution: 1 Variable with None value\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " If you still have issues converging your model consider:\n",
+ " prepare_svd_toolbox()\n",
+ " prepare_degeneracy_hunter()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
}
+ ],
+ "source": [
+ "dt_scaled = DiagnosticsToolbox(scaled_model)\n",
+ "dt_scaled.report_numerical_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We can see that applying scaling addressed two of the cautions we had before (the variable with an extreme value and an associated large value in the model Jacobian). Whilst we were able to solve the unscaled model in this case, this is in part because it was a simple linear model. In more complex, non-linear models, scaling becomes much more important and often depends strongly on the current state of the model. That is, you can often find cases where the unscaled (or poorly scaled) model solves for a limited range of conditions but fails to solve if you move too far away for the current state. Whilst you might be able to solve the model at the current state, you should always check the solver logs and numerical cautions for advanced warning signs of scaling issues that might manifest later when you try to solve the model for a different state (e.g., during optimization).\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "By their nature, numerical issues depend on the current values of the variables in the model, and thus may remain hidden until someone tries to solve the model close to where the issue exists. For this reason, the full model diagnostics workflow contains steps to run the numerical checks across a wide range of variable values to try to ensure that no issues remain hidden. This is beyond the scope of this tutorial however.\n",
+ "
\n",
+ "\n",
+ "At this point, we have addressed all the issues that were preventing us from solving the demonstration model and so reached the end of this tutorial. For cases where we are still having trouble solving the model, we can see that the toolbox is suggesting additional methods for further debugging and these advanced features will be the focus of separate tutorials."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 47,
+ "metadata": {
+ "tags": [
+ "testing"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "dt.assert_no_structural_warnings()\n",
+ "dt.assert_no_numerical_warnings()"
+ ]
+ }
+ ],
+ "metadata": {
+ "celltoolbar": "Tags",
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
},
- "nbformat": 4,
- "nbformat_minor": 3
+ "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
}
\ No newline at end of file
diff --git a/idaes_examples/notebooks/docs/diagnostics/diagnostics_toolbox_usr.ipynb b/idaes_examples/notebooks/docs/diagnostics/diagnostics_toolbox_usr.ipynb
index 9ea0c9bc..688e5148 100644
--- a/idaes_examples/notebooks/docs/diagnostics/diagnostics_toolbox_usr.ipynb
+++ b/idaes_examples/notebooks/docs/diagnostics/diagnostics_toolbox_usr.ipynb
@@ -1,2104 +1,2106 @@
{
- "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": [
- "# IDAES Model Diagnostics Toolbox Tutorial\n",
- "Author: Andrew Lee \n",
- "Maintainer: Andrew Lee \n",
- "Updated: 2023-10-31 \n",
- "\n",
- "As you have likely discovered already, developing and solving models in an equation-oriented (EO) environment can be challenging and often takes a significant amount of effort. There are many pitfalls and mistakes that can be encountered when developing a model which can greatly impact the solvability and robustness of the final problem.\n",
- "\n",
- "Model diagnosis and debugging is often more of an art than a science, and it generally relies on significant experience and understanding both of general EO modeling techniques and the specific model and problem being solved. To assist with this process, IDAES has developed a model diagnostics toolbox that brings together a large number of tools for identifying potential issues in a model to help guide the user through the process of finding and resolving these issues. Note however that whilst these tools can help identify the presence of an issue, remedying the issue always requires some degree of engineering knowledge about the system being modeled, and thus it is ultimately up to the user to find a solution to the problem.\n",
- "\n",
- "This tutorial will take you through using the ``DiagnosticsToolbox`` to debug a number of issues in a simple Pyomo model and to take it from initially reporting a possible infeasible solution to returning the correct solution.\n",
- "\n",
- "To get started, the ``DiagnosticsToolbox`` can be imported from ``idaes.core.util``.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Import the DiagnosticsToolbox in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "from idaes.core.util import DiagnosticsToolbox"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "To get some information on where to start, try using the Python ``help()`` function to see the documentation for the ``DiagnosticsToolbox``.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Call `help(DiagnosticsToolbox)` to see some more information on the toolbox and some instructions on how to get started.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Call the help() function for more information"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Help on class DiagnosticsToolbox in module idaes.core.util.model_diagnostics:\n",
- "\n",
- "class DiagnosticsToolbox(builtins.object)\n",
- " | DiagnosticsToolbox(model: pyomo.core.base.block._BlockData, **kwargs)\n",
- " | \n",
- " | The IDAES Model DiagnosticsToolbox.\n",
- " | \n",
- " | To get started:\n",
- " | \n",
- " | 1. Create an instance of your model (this does not need to be initialized yet).\n",
- " | 2. Fix variables until you have 0 degrees of freedom. Many of these tools presume\n",
- " | a square model, and a square model should always be the foundation of any more\n",
- " | advanced model.\n",
- " | 3. Create an instance of the DiagnosticsToolbox and provide the model to debug as\n",
- " | the model argument.\n",
- " | 4. Call the ``report_structural_issues()`` method.\n",
- " | \n",
- " | Model diagnostics is an iterative process and you will likely need to run these\n",
- " | tools multiple times to resolve all issues. After making a change to your model,\n",
- " | you should always start from the beginning again to ensure the change did not\n",
- " | introduce any new issues; i.e., always start from the report_structural_issues()\n",
- " | method.\n",
- " | \n",
- " | Note that structural checks do not require the model to be initialized, thus users\n",
- " | should start with these. Numerical checks require at least a partial solution to the\n",
- " | model and should only be run once all structural issues have been resolved.\n",
- " | \n",
- " | Report methods will print a summary containing three parts:\n",
- " | \n",
- " | 1. Warnings - these are critical issues that should be resolved before continuing.\n",
- " | For each warning, a method will be suggested in the Next Steps section to get\n",
- " | additional information.\n",
- " | 2. Cautions - these are things that could be correct but could also be the source of\n",
- " | solver issues. Not all cautions need to be addressed, but users should investigate\n",
- " | each one to ensure that the behavior is correct and that they will not be the source\n",
- " | of difficulties later. Methods exist to provide more information on all cautions,\n",
- " | but these will not appear in the Next Steps section.\n",
- " | 3. Next Steps - these are recommended methods to call from the DiagnosticsToolbox to\n",
- " | get further information on warnings. If no warnings are found, this will suggest\n",
- " | the next report method to call.\n",
- " | \n",
- " | Args:\n",
- " | \n",
- " | model: model to be diagnosed. The DiagnosticsToolbox does not support indexed Blocks.\n",
- " | \n",
- " | Keyword Arguments\n",
- " | -----------------\n",
- " | variable_bounds_absolute_tolerance: float, default=0.0001\n",
- " | Absolute tolerance for considering a variable to be close to its\n",
- " | bounds.\n",
- " | \n",
- " | variable_bounds_relative_tolerance: float, default=0.0001\n",
- " | Relative tolerance for considering a variable to be close to its\n",
- " | bounds.\n",
- " | \n",
- " | variable_bounds_violation_tolerance: float, default=0\n",
- " | Absolute tolerance for considering a variable to violate its bounds.\n",
- " | Some solvers relax bounds on variables thus allowing a small violation\n",
- " | to be considered acceptable.\n",
- " | \n",
- " | constraint_residual_tolerance: float, default=1e-05\n",
- " | Absolute tolerance to use when checking constraint residuals.\n",
- " | \n",
- " | variable_large_value_tolerance: float, default=10000.0\n",
- " | Absolute tolerance for considering a value to be large.\n",
- " | \n",
- " | variable_small_value_tolerance: float, default=0.0001\n",
- " | Absolute tolerance for considering a value to be small.\n",
- " | \n",
- " | variable_zero_value_tolerance: float, default=1e-08\n",
- " | Absolute tolerance for considering a value to be near to zero.\n",
- " | \n",
- " | jacobian_large_value_caution: float, default=10000.0\n",
- " | Tolerance for raising a caution for large Jacobian values.\n",
- " | \n",
- " | jacobian_large_value_warning: float, default=100000000.0\n",
- " | Tolerance for raising a warning for large Jacobian values.\n",
- " | \n",
- " | jacobian_small_value_caution: float, default=0.0001\n",
- " | Tolerance for raising a caution for small Jacobian values.\n",
- " | \n",
- " | jacobian_small_value_warning: float, default=1e-08\n",
- " | Tolerance for raising a warning for small Jacobian values.\n",
- " | \n",
- " | warn_for_evaluation_error_at_bounds: bool, default=True\n",
- " | If False, warnings will not be generated for things like log(x) with x\n",
- " | >= 0\n",
- " | \n",
- " | Methods defined here:\n",
- " | \n",
- " | __init__(self, model: pyomo.core.base.block._BlockData, **kwargs)\n",
- " | Initialize self. See help(type(self)) for accurate signature.\n",
- " | \n",
- " | assert_no_numerical_warnings(self)\n",
- " | Checks for numerical warnings in the model and raises an AssertionError\n",
- " | if any are found.\n",
- " | \n",
- " | Raises:\n",
- " | AssertionError if any warnings are identified by numerical analysis.\n",
- " | \n",
- " | assert_no_structural_warnings(self)\n",
- " | Checks for structural warnings in the model and raises an AssertionError\n",
- " | if any are found.\n",
- " | \n",
- " | Raises:\n",
- " | AssertionError if any warnings are identified by structural analysis.\n",
- " | \n",
- " | display_components_with_inconsistent_units(self, stream=None)\n",
- " | Prints a list of all Constraints, Expressions and Objectives in the\n",
- " | model with inconsistent units of measurement.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_constraints_with_extreme_jacobians(self, stream=None)\n",
- " | Prints the constraints associated with rows in the Jacobian with extreme\n",
- " | L2 norms. This often indicates poorly scaled constraints.\n",
- " | \n",
- " | Tolerances can be set via the DiagnosticsToolbox config.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the output to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_constraints_with_large_residuals(self, stream=None)\n",
- " | Prints a list of Constraints with residuals greater than a specified tolerance.\n",
- " | Tolerance can be set in the class configuration options.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_external_variables(self, stream=None)\n",
- " | Prints a list of variables that appear within activated Constraints in the\n",
- " | model but are not contained within the model themselves.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_extreme_jacobian_entries(self, stream=None)\n",
- " | Prints variables and constraints associated with entries in the Jacobian with extreme\n",
- " | values. This can be indicative of poor scaling, especially for isolated terms (e.g.\n",
- " | variables which appear only in one term of a single constraint).\n",
- " | \n",
- " | Tolerances can be set via the DiagnosticsToolbox config.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the output to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_overconstrained_set(self, stream=None)\n",
- " | Prints the variables and constraints in the over-constrained sub-problem\n",
- " | from a Dulmage-Mendelsohn partitioning.\n",
- " | \n",
- " | This can be used to identify the over-defined part of a model and thus\n",
- " | where constraints must be removed or variables unfixed.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_potential_evaluation_errors(self, stream=None)\n",
- " | Prints constraints that may be prone to evaluation errors\n",
- " | (e.g., log of a negative number) based on variable bounds.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the output to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_underconstrained_set(self, stream=None)\n",
- " | Prints the variables and constraints in the under-constrained sub-problem\n",
- " | from a Dulmage-Mendelsohn partitioning.\n",
- " | \n",
- " | This can be used to identify the under-defined part of a model and thus\n",
- " | where additional information (fixed variables or constraints) are required.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_unused_variables(self, stream=None)\n",
- " | Prints a list of variables that do not appear in any activated Constraints.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_variables_at_or_outside_bounds(self, stream=None)\n",
- " | Prints a list of variables with values that fall at or outside the bounds\n",
- " | on the variable.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_variables_fixed_to_zero(self, stream=None)\n",
- " | Prints a list of variables that are fixed to an absolute value of 0.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_variables_near_bounds(self, stream=None)\n",
- " | Prints a list of variables with values close to their bounds. Tolerance can\n",
- " | be set in the class configuration options.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_variables_with_extreme_jacobians(self, stream=None)\n",
- " | Prints the variables associated with columns in the Jacobian with extreme\n",
- " | L2 norms. This often indicates poorly scaled variables.\n",
- " | \n",
- " | Tolerances can be set via the DiagnosticsToolbox config.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the output to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_variables_with_extreme_values(self, stream=None)\n",
- " | Prints a list of variables with extreme values.\n",
- " | \n",
- " | Tolerances can be set in the class configuration options.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_variables_with_none_value(self, stream=None)\n",
- " | Prints a list of variables with a value of None.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | display_variables_with_value_near_zero(self, stream=None)\n",
- " | Prints a list of variables with a value close to zero. The tolerance\n",
- " | for determining what is close to zero can be set in the class configuration\n",
- " | options.\n",
- " | \n",
- " | Args:\n",
- " | stream: an I/O object to write the list to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | get_dulmage_mendelsohn_partition(self)\n",
- " | Performs a Dulmage-Mendelsohn partitioning on the model and returns\n",
- " | the over- and under-constrained sub-problems.\n",
- " | \n",
- " | Returns:\n",
- " | list-of-lists variables in each independent block of the under-constrained set\n",
- " | list-of-lists constraints in each independent block of the under-constrained set\n",
- " | list-of-lists variables in each independent block of the over-constrained set\n",
- " | list-of-lists constraints in each independent block of the over-constrained set\n",
- " | \n",
- " | prepare_degeneracy_hunter(self, **kwargs)\n",
- " | Create an instance of the DegeneracyHunter and store as self.degeneracy_hunter.\n",
- " | \n",
- " | After creating an instance of the toolbox, call\n",
- " | report_irreducible_degenerate_sets.\n",
- " | \n",
- " | Returns:\n",
- " | \n",
- " | Instance of DegeneracyHunter\n",
- " | \n",
- " | Keyword Arguments\n",
- " | -----------------\n",
- " | solver: str, default='scip'\n",
- " | MILP solver to use for finding irreducible degenerate sets.\n",
- " | \n",
- " | solver_options: optional\n",
- " | Options to pass to MILP solver.\n",
- " | \n",
- " | M: float, default=100000.0\n",
- " | Maximum value for nu in MILP models.\n",
- " | \n",
- " | m_small: float, default=1e-05\n",
- " | Smallest value for nu to be considered non-zero in MILP models.\n",
- " | \n",
- " | trivial_constraint_tolerance: float, default=1e-06\n",
- " | Tolerance for identifying non-zero rows in Jacobian.\n",
- " | \n",
- " | prepare_svd_toolbox(self, **kwargs)\n",
- " | Create an instance of the SVDToolbox and store as self.svd_toolbox.\n",
- " | \n",
- " | After creating an instance of the toolbox, call\n",
- " | display_underdetermined_variables_and_constraints().\n",
- " | \n",
- " | Returns:\n",
- " | \n",
- " | Instance of SVDToolbox\n",
- " | \n",
- " | Keyword Arguments\n",
- " | -----------------\n",
- " | number_of_smallest_singular_values: PositiveInt, optional\n",
- " | Number of smallest singular values to compute\n",
- " | \n",
- " | svd_callback: svd_callback_validator, default=\n",
- " | Callback to SVD method of choice (default = svd_dense). Callbacks\n",
- " | should take the Jacobian and number of singular values to compute as\n",
- " | options, plus any method specific arguments, and should return the u,\n",
- " | s and v matrices as numpy arrays.\n",
- " | \n",
- " | svd_callback_arguments: dict, optional\n",
- " | Optional arguments to pass to SVD callback (default = None)\n",
- " | \n",
- " | singular_value_tolerance: float, default=1e-06\n",
- " | Tolerance for defining a small singular value\n",
- " | \n",
- " | size_cutoff_in_singular_vector: float, default=0.1\n",
- " | Size below which to ignore constraints and variables in the singular\n",
- " | vector\n",
- " | \n",
- " | report_numerical_issues(self, stream=None)\n",
- " | Generates a summary report of any numerical issues identified in the model provided\n",
- " | and suggest next steps for debugging model.\n",
- " | \n",
- " | Numerical checks should only be performed once all structural issues have been resolved,\n",
- " | and require that at least a partial solution to the model is available.\n",
- " | \n",
- " | Args:\n",
- " | stream: I/O object to write report to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | report_structural_issues(self, stream=None)\n",
- " | Generates a summary report of any structural issues identified in the model provided\n",
- " | and suggests next steps for debugging the model.\n",
- " | \n",
- " | This should be the first method called when debugging a model and after any change\n",
- " | is made to the model. These checks can be run before trying to initialize and solve\n",
- " | the model.\n",
- " | \n",
- " | Args:\n",
- " | stream: I/O object to write report to (default = stdout)\n",
- " | \n",
- " | Returns:\n",
- " | None\n",
- " | \n",
- " | ----------------------------------------------------------------------\n",
- " | Readonly properties defined here:\n",
- " | \n",
- " | model\n",
- " | Model currently being diagnosed.\n",
- " | \n",
- " | ----------------------------------------------------------------------\n",
- " | Data descriptors defined here:\n",
- " | \n",
- " | __dict__\n",
- " | dictionary for instance variables (if defined)\n",
- " | \n",
- " | __weakref__\n",
- " | list of weak references to the object (if defined)\n",
- "\n"
- ]
- }
- ],
- "source": [
- "help(DiagnosticsToolbox)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The ``help()`` function gives us a lot of information on the ``DiagnosticsToolbox`` and all the methods that it supports (and there are many). However, the important part to start with are the four steps outlined at the top of the doc string that tell us how to get started.\n",
- "\n",
- "Firstly, we need a model to test (and, for this tutorial at least, one that has a wide range of issues that we need to fix before it will solve). We then also need to fix some variables so that we have 0 degrees of freedom in our model. Whilst our ultimate goal is generally optimization (and thus a system with 1 or more degrees of freedom), all models conceptually derive from a square model representing a nominal state. If this nominal state is not well-posed, then any issues present will also be present in the resulting optimization (even if adding degrees of freedom means that the model is now easier to solve).\n",
- "\n",
- "The cell below contains a demonstration model for this tutorial that contains a number of issues that we will resolve using the ``DiagnosticsToolbox``."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [],
- "source": [
- "import pyomo.environ as pyo\n",
- "\n",
- "m = pyo.ConcreteModel()\n",
- "\n",
- "m.v1 = pyo.Var(units=pyo.units.m)\n",
- "m.v2 = pyo.Var(units=pyo.units.m)\n",
- "m.v3 = pyo.Var(bounds=(0, 5))\n",
- "m.v4 = pyo.Var()\n",
- "m.v5 = pyo.Var(bounds=(0, 10))\n",
- "m.v6 = pyo.Var()\n",
- "m.v7 = pyo.Var(units=pyo.units.m, bounds=(0, 1)) # Poorly scaled variable with lower bound\n",
- "m.v8 = pyo.Var() # unused variable\n",
- "\n",
- "m.c1 = pyo.Constraint(expr=m.v1 + m.v2 == 10) # Unit consistency issue\n",
- "m.c2 = pyo.Constraint(expr=m.v3 == m.v4 + m.v5)\n",
- "m.c3 = pyo.Constraint(expr=2*m.v3 == 3*m.v4 + 4*m.v5 + m.v6)\n",
- "m.c4 = pyo.Constraint(expr=m.v7 == 1e-8*m.v1) # Poorly scaled constraint\n",
- "\n",
- "m.v4.fix(2)\n",
- "m.v5.fix(2)\n",
- "m.v6.fix(0)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Next, the instructions tell us to create an instance of the ``DiagnosticsToolbox`` and to pass the model we wish to examine as an argument.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Create an instance of the DiagnosticsToolbox: dt = DiagnosticsToolbox(m)\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Create an instance of the Diagnostics Toolbox"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "dt = DiagnosticsToolbox(m)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Finally, the instructions tell us to run the ``report_structural_issues()`` method. Structural issues represent issues that exist solely in the form of the model equations and thus do not depend on the current value of any of the variables. This is useful as it means we can check for these before we even call a solver, which can be critical as sometimes these issues will cause a solver to fail without providing a useful solution.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Call dt.report_structural_issues() in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Call the report_strucutral_issues() method"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Model Statistics\n",
- "\n",
- " Activated Blocks: 1 (Deactivated: 0)\n",
- " Free Variables in Activated Constraints: 4 (External: 0)\n",
- " Free Variables with only lower bounds: 0\n",
- " Free Variables with only upper bounds: 0\n",
- " Free Variables with upper and lower bounds: 2\n",
- " Fixed Variables in Activated Constraints: 3 (External: 0)\n",
- " Activated Equality Constraints: 4 (Deactivated: 0)\n",
- " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
- " Activated Objectives: 0 (Deactivated: 0)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "2 WARNINGS\n",
- "\n",
- " WARNING: 1 Component with inconsistent units\n",
- " WARNING: Structural singularity found\n",
- " Under-Constrained Set: 3 variables, 2 constraints\n",
- " Over-Constrained Set: 1 variables, 2 constraints\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "2 Cautions\n",
- "\n",
- " Caution: 1 variable fixed to 0\n",
- " Caution: 1 unused variable (0 fixed)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "Suggested next steps:\n",
- "\n",
- " display_components_with_inconsistent_units()\n",
- " display_underconstrained_set()\n",
- " display_overconstrained_set()\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.report_structural_issues()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Looking at the output from the ``report_structural_issues()`` method, we can see that it provides a fairly short summary containing 4 sections.\n",
- "\n",
- "1. The first section is a summary of the size of the model, indicating things like the number of variables and constraints. The size of the model is often important for judging how difficult it will be to solve, and this information can also be useful for comparison to what is being sent to the solver. Most solvers will report the size of the model in their output logs, and if there is a difference between what is reported here and by the solver, then you should probably look into what is happening. This section also notes some things such as if you have any deactivated Blocks, Constraints or Objectives, or if you have variables which appear in the constraints that are not part of the model; these are not necessarily wrong but it is easy to have accidentally deactivated something you did not intend to so you should always check to see that these are expected.\n",
- "\n",
- "2. The second section provides a summary of any critical structural issues that were found - in this case we can see that there are 2 warnings we are going to need to look into. Warnings represent issues that need to be addressed before moving on as these will likely cause the solver to fail or give an incorrect answer.\n",
- "\n",
- "3. The third section lists a summary of any cautions that are found. Cautions represent issues that may or may not be problematic; in many cases these might be expected behaviors or borderline issues. However, these could also represent conceptual issues that should be addressed, so users should take the time to investigate these and determine if they need to be fixed or not.\n",
- "\n",
- "4. Finally, there is a section that suggests the next steps to take to help guide you through the model diagnosis process. If any warnings were identified, this section will list methods that can help you get more information on each specific problem, and if no warnings are found then it will guide you onto the next step in the model diagnosis workflow.\n",
- "\n",
- "**Note:** there are methods available to help investigate cautions as well, but these will not show up in the next steps in order to avoid cluttering the output. You can get more information on the available methods for investigating cautions via the documentation or ``help()`` function.\n",
- "\n",
- "In our current model, we have 2 critical issues (warnings) that we need to look into and resolve. The order in which we resolve these will generally not matter, but be aware that these can often be interrelated - fixing one warning might resolve other warnings as well (or create new ones), and sometimes you will need to look at multiple issues together to find the overall root cause.\n",
- "\n",
- "To start with, let us look at the unit consistency issue. From the \"Next Steps\" section above, the toolbox is suggesting we run the ``display_components_with_inconsistent_units()`` method for more information.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Call the `display_components_with_inconsistent_units()` method from the DiagnosticsToolbox to see more information on which constraint is causing the unit consistency issues.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Call the display_components_with_inconsistent_units() method"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 11,
- "metadata": {
- "scrolled": false,
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "The following component(s) have unit consistency issues:\n",
- "\n",
- " c1\n",
- "\n",
- "For more details on unit inconsistencies, import the assert_units_consistent method\n",
- "from pyomo.util.check_units\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.display_components_with_inconsistent_units()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "This tells us that the issue lies in constraint ``c1``. If we go back and look at this constraint, we can see that it says ``v1 + v2 == 10``. ``v1`` and ``v2`` both have units of ``m`` which is consistent, but the constant in the expression (right hand side) is unitless. Thus, we need to correct this so that the right hand side has units for the constraint to be consistent.\n",
- "\n",
- "The cell below shows how to delete a constraint and replace it with a new one with the correct units.\n",
- "\n",
- "\n",
- "Warning:\n",
- "Deleting components can cause unexpected issues if something else in a model is using that component (e.g., deleting a variable which is used in a constraint). You should always be careful when deleting Pyomo components and make sure you only delete components that are not used elsewhere.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 12,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Delete the incorrect Constraint\n",
- "m.del_component(m.c1)\n",
- "\n",
- "# Re-create the Constraint with the correct units\n",
- "m.c1 = pyo.Constraint(expr=m.v1 + m.v2 == 10*pyo.units.m)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Warning:\n",
- "Fixing issues in models is often an iterative process requiring trial and error. You might also have some results from a model before running the diagnostics tools and the changes you make during debugging may make it difficult to replicate those results afterwards.\n",
- " \n",
- "It is strongly recommended that you keep a record of the changes you make at each step and why, along with a Git hash (or similar version control marker) corresponding to these changes. This will allow you see what changes and why, and give you a way to go back to previous iterations if the current approach does not work out. The IDAES documentation contains recommendations on how to keep and maintain a modeling logbook.\n",
- "
\n",
- "\n",
- "Now, re-run the ``report_structural_issues()`` method and see if this change has fixed the unit consistency issue.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Call dt.report_structural_issues() in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 13,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Call the report_structural_issues() method"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 14,
- "metadata": {
- "scrolled": true,
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Model Statistics\n",
- "\n",
- " Activated Blocks: 1 (Deactivated: 0)\n",
- " Free Variables in Activated Constraints: 4 (External: 0)\n",
- " Free Variables with only lower bounds: 0\n",
- " Free Variables with only upper bounds: 0\n",
- " Free Variables with upper and lower bounds: 2\n",
- " Fixed Variables in Activated Constraints: 3 (External: 0)\n",
- " Activated Equality Constraints: 4 (Deactivated: 0)\n",
- " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
- " Activated Objectives: 0 (Deactivated: 0)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "1 WARNINGS\n",
- "\n",
- " WARNING: Structural singularity found\n",
- " Under-Constrained Set: 3 variables, 2 constraints\n",
- " Over-Constrained Set: 1 variables, 2 constraints\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "2 Cautions\n",
- "\n",
- " Caution: 1 variable fixed to 0\n",
- " Caution: 1 unused variable (0 fixed)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "Suggested next steps:\n",
- "\n",
- " display_underconstrained_set()\n",
- " display_overconstrained_set()\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.report_structural_issues()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The unit consistency issue has been resolved by the changes above, so now we need to look at the structural singularity. A structural singularity occurs when one sub-part of the model is over-constrained (negative degrees of freedom), which generally means another part is under-constrained (positive degrees of freedom, assuming that there are 0 degrees of freedom overall).\n",
- "\n",
- "The toolbox is suggesting we use the ``display_overconstrained_set()`` and ``display_underconstrained_set()`` methods to get more information on the singularity; for now, let us start with the over-constrained set.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Call dt.display_overconstrained_set() in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 15,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Call the display_overconstrained_set() method"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 16,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Dulmage-Mendelsohn Over-Constrained Set\n",
- "\n",
- " Independent Block 0:\n",
- "\n",
- " Variables:\n",
- "\n",
- " v3\n",
- "\n",
- " Constraints:\n",
- "\n",
- " c2\n",
- " c3\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.display_overconstrained_set()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "From the output above, the toolbox is telling us that we have two constraints (``c2`` and ``c3``) which only contain a single unfixed variable (``v3``); thus in this part of the model we have -1 degree of freedom and the model is not well defined (structurally singular). If we go back and look at these constraints, we can see the that the constraints are:\n",
- "\n",
- "``c2: v3 == v4 + v5``\n",
- "\n",
- "``c3: 2*v3 == 3*v4 + 4*v5 + v6``\n",
- "\n",
- "We can see that in addition to ``v3`` these constraints actually contain 3 other variables (``v4``, ``v5`` and ``v6``), however these are all variables we fixed to get our initial zero degrees of freedom. It looks like we have either accidentally fixed one too many variables or written one too many constraints.\n",
- "\n",
- "For this example, let us assume that ``v4`` was not supposed to be fixed and unfix it.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Resolve the structural singularity and then call dt.report_structural_issues() in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 17,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Unfix v4\n",
- "\n",
- "# Then call the report_structural_issues() method again"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 18,
- "metadata": {
- "scrolled": true,
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Model Statistics\n",
- "\n",
- " Activated Blocks: 1 (Deactivated: 0)\n",
- " Free Variables in Activated Constraints: 5 (External: 0)\n",
- " Free Variables with only lower bounds: 0\n",
- " Free Variables with only upper bounds: 0\n",
- " Free Variables with upper and lower bounds: 2\n",
- " Fixed Variables in Activated Constraints: 2 (External: 0)\n",
- " Activated Equality Constraints: 4 (Deactivated: 0)\n",
- " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
- " Activated Objectives: 0 (Deactivated: 0)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "2 WARNINGS\n",
- "\n",
- " WARNING: 1 Degree of Freedom\n",
- " WARNING: Structural singularity found\n",
- " Under-Constrained Set: 3 variables, 2 constraints\n",
- " Over-Constrained Set: 0 variables, 0 constraints\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "2 Cautions\n",
- "\n",
- " Caution: 1 variable fixed to 0\n",
- " Caution: 1 unused variable (0 fixed)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "Suggested next steps:\n",
- "\n",
- " display_underconstrained_set()\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "m.v4.unfix()\n",
- "\n",
- "dt.report_structural_issues()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We can see that the over-constrained set is now empty (0 variables and 0 constraints) but the under-constrained set still has 3 variables and only 2 constraints. We can also see that there is a new warning about having 1 degree of freedom in the model, however this should not be surprising as we have just unfixed ``v4`` to resolve the over-constrained set so we have added a degree of freedom to the model.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Display the under-constrained set in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 19,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Display the under-constrained set"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 20,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Dulmage-Mendelsohn Under-Constrained Set\n",
- "\n",
- " Independent Block 0:\n",
- "\n",
- " Variables:\n",
- "\n",
- " v2\n",
- " v1\n",
- " v7\n",
- "\n",
- " Constraints:\n",
- "\n",
- " c1\n",
- " c4\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.display_underconstrained_set()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Looking at the output from the ``display_underconstrained_set()`` method, we can see that we have two constraints, ``c1`` and ``c4``, which contain three unfixed variables, ``v1``, ``v2`` and ``v7``. Thus, we have one degree of freedom that needs to be addressed. To fix this, we could either fix one of the variables shown or add an additional equality constraint to the model.\n",
- "\n",
- "For this example let's fix ``v2`` to a value of 5 and then re-run the ``report_structural_issues()`` method.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Fix v2 to a value of 5 and then re-run dt.report_structural_issues.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 21,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Fix v2 = 5\n",
- "\n",
- "# Then re-run report_structural_issues() method"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 22,
- "metadata": {
- "scrolled": true,
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Model Statistics\n",
- "\n",
- " Activated Blocks: 1 (Deactivated: 0)\n",
- " Free Variables in Activated Constraints: 4 (External: 0)\n",
- " Free Variables with only lower bounds: 0\n",
- " Free Variables with only upper bounds: 0\n",
- " Free Variables with upper and lower bounds: 2\n",
- " Fixed Variables in Activated Constraints: 3 (External: 0)\n",
- " Activated Equality Constraints: 4 (Deactivated: 0)\n",
- " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
- " Activated Objectives: 0 (Deactivated: 0)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "0 WARNINGS\n",
- "\n",
- " No warnings found!\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "2 Cautions\n",
- "\n",
- " Caution: 1 variable fixed to 0\n",
- " Caution: 1 unused variable (0 fixed)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "Suggested next steps:\n",
- "\n",
- " Try to initialize/solve your model and then call report_numerical_issues()\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "m.v2.fix(5)\n",
- "\n",
- "dt.report_structural_issues()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The toolbox is now telling us that no warnings were found, so we have resolved all the structural issues (for now at least). The toolbox is telling us that there are also 2 non-critical issues (cautions) that we should look at; one about an unused variable and one about a variable fixed to zero. If you wish, you can look into identifying and fixing these yourself, however for this example we will move on to the next step (remember that the toolbox has methods to display more details for each of these which you can find in the documentation or from the ``help()`` function).\n",
- "\n",
- "For the Next Steps section, the toolbox is recommending we try to solve our model and then check for numerical issues.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Use the Pyomo SolverFactory to create an instance of IPOPT and then try to solve the model. Make sure to set \"tee=True\" as this is going to fail (and it is always good practice to review the solver logs).\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 23,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Create a solver object\n",
- "\n",
- "# Try to solve the model"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 24,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "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...: 7\n",
- "Number of nonzeros in inequality constraint Jacobian.: 0\n",
- "Number of nonzeros in Lagrangian Hessian.............: 0\n",
- "\n",
- "Total number of variables............................: 4\n",
- " variables with only lower bounds: 0\n",
- " variables with lower and upper bounds: 2\n",
- " variables with only upper bounds: 0\n",
- "Total number of equality constraints.................: 4\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 1.40e+01 0.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
- " 1 0.0000000e+00 1.39e+01 1.50e+02 -1.0 6.00e+00 - 7.16e-01 4.93e-03h 1\n",
- " 2 0.0000000e+00 1.39e+01 3.03e+06 -1.0 5.97e+00 - 1.00e+00 4.95e-05h 1\n",
- " 3r 0.0000000e+00 1.39e+01 1.00e+03 1.1 0.00e+00 - 0.00e+00 2.47e-07R 2\n",
- " 4r 0.0000000e+00 4.19e+00 9.42e+02 1.1 3.50e+03 - 4.02e-01 3.37e-03f 1\n",
- " 5r 0.0000000e+00 2.12e+00 8.72e+02 1.1 5.89e+01 - 4.35e-01 7.06e-02f 1\n",
- " 6r 0.0000000e+00 6.74e-01 6.06e+02 1.1 5.29e+00 - 9.93e-03 3.98e-01f 1\n",
- " 7r 0.0000000e+00 6.80e-01 3.14e+02 0.4 2.05e-01 - 1.00e+00 1.03e-01f 1\n",
- " 8r 0.0000000e+00 6.69e-01 2.78e-05 0.4 2.58e-02 - 1.00e+00 1.00e+00f 1\n",
- " 9r 0.0000000e+00 6.67e-01 7.56e+00 -1.7 8.13e-03 - 9.93e-01 9.96e-01f 1\n",
- "iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n",
- " 10r 0.0000000e+00 6.67e-01 2.23e-07 -1.7 4.13e-05 - 1.00e+00 1.00e+00f 1\n",
- " 11r 0.0000000e+00 6.67e-01 6.73e-01 -3.7 6.61e-05 - 1.00e+00 1.00e+00f 1\n",
- " 12r 0.0000000e+00 6.67e-01 1.91e-09 -3.7 1.48e-09 - 1.00e+00 1.00e+00h 1\n",
- " 13r 0.0000000e+00 6.67e-01 2.69e+00 -8.4 5.74e-07 - 1.00e+00 9.26e-01f 1\n",
- " 14r 0.0000000e+00 6.67e-01 7.65e+01 -8.4 4.23e-08 - 8.68e-01 1.00e+00f 1\n",
- "\n",
- "Number of Iterations....: 14\n",
- "\n",
- " (scaled) (unscaled)\n",
- "Objective...............: 0.0000000000000000e+00 0.0000000000000000e+00\n",
- "Dual infeasibility......: 3.2644919411246030e-04 3.2644919411246030e-04\n",
- "Constraint violation....: 6.6666666333656233e-01 6.6666666333656233e-01\n",
- "Complementarity.........: 4.6615546565561981e-09 4.6615546565561981e-09\n",
- "Overall NLP error.......: 6.6666666333656233e-01 6.6666666333656233e-01\n",
- "\n",
- "\n",
- "Number of objective function evaluations = 18\n",
- "Number of objective gradient evaluations = 5\n",
- "Number of equality constraint evaluations = 18\n",
- "Number of inequality constraint evaluations = 0\n",
- "Number of equality constraint Jacobian evaluations = 17\n",
- "Number of inequality constraint Jacobian evaluations = 0\n",
- "Number of Lagrangian Hessian evaluations = 15\n",
- "Total CPU secs in IPOPT (w/o function evaluations) = 0.003\n",
- "Total CPU secs in NLP function evaluations = 0.000\n",
- "\n",
- "EXIT: Converged to a point of local infeasibility. Problem may be infeasible.\n",
- "WARNING: Loading a SolverResults object with a warning status into\n",
- "model.name=\"unknown\";\n",
- " - termination condition: infeasible\n",
- " - message from solver: Ipopt 3.13.2\\x3a Converged to a locally infeasible\n",
- " point. Problem may be infeasible.\n"
- ]
- },
- {
- "data": {
- "text/plain": [
- "{'Problem': [{'Lower bound': -inf, 'Upper bound': inf, 'Number of objectives': 1, 'Number of constraints': 4, 'Number of variables': 4, 'Sense': 'unknown'}], 'Solver': [{'Status': 'warning', 'Message': 'Ipopt 3.13.2\\\\x3a Converged to a locally infeasible point. Problem may be infeasible.', 'Termination condition': 'infeasible', 'Id': 200, 'Error rc': 0, 'Time': 0.007064104080200195}], 'Solution': [OrderedDict([('number of solutions', 0), ('number of solutions displayed', 0)])]}"
- ]
- },
- "execution_count": 24,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "solver = pyo.SolverFactory('ipopt')\n",
- "solver.solve(m, tee=True)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "As hinted at above, IPOPT has returned a warning that the problem may be infeasible. Before moving on however, it is always good practice to look over the solver outputs and see what it is telling you.\n",
- "\n",
- "\n",
- "Warning:\n",
- "A lot of useful information is contained in the solver logs which is extremely useful when diagnosing modeling issues. Each solver has its own way of reporting output and its own specific behavior, so you will need to learn to interpret the output of each solver you use. The IDAES Documentation contains some guidance on interpreting output logs for a few common solvers.\n",
- "
\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Call the report_numerical_issues method in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 25,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Check for numerical issues"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 26,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Model Statistics\n",
- "\n",
- " Jacobian Condition Number: 1.700E+01\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "2 WARNINGS\n",
- "\n",
- " WARNING: 1 Constraint with large residuals (>1.0E-05)\n",
- " WARNING: 1 Variable at or outside bounds (tol=0.0E+00)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "5 Cautions\n",
- "\n",
- " Caution: 2 Variables with value close to their bounds (abs=1.0E-04, rel=1.0E-04)\n",
- " Caution: 2 Variables with value close to zero (tol=1.0E-08)\n",
- " Caution: 1 Variable with extreme value (<1.0E-04 or >1.0E+04)\n",
- " Caution: 1 Variable with None value\n",
- " Caution: 1 extreme Jacobian Entry (<1.0E-04 or >1.0E+04)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "Suggested next steps:\n",
- "\n",
- " display_constraints_with_large_residuals()\n",
- " display_variables_at_or_outside_bounds()\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.report_numerical_issues()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The ``report_numerical_issues()`` provides a summary similar to that which we saw for the structural issues. Firstly, it reports to us the Jacobian condition number for our problem which can give us an idea of how well-scaled the problem is, followed by a list of warnings, cautions and suggested next steps.\n",
- "\n",
- "Unsurprisingly, we are seeing a warning about a constraint with a large residual which we would expect when a solver reports a potentially infeasible problem. We are also seeing a warning about a variable with bound violations which might be contributing to the potential infeasibility.\n",
- "\n",
- "For the next steps, the toolbox is suggesting some new methods to get more information on these issues; let us start by looking at the constraints with large residuals.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Display the constraint with a large residual in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 27,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Display constraint with large residual"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 28,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "The following constraint(s) have large residuals (>1.0E-05):\n",
- "\n",
- " c2: 6.66667E-01\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.display_constraints_with_large_residuals()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The toolbox is telling us that the constraint which failed to converge is ``c2``, however this is generally only part of the story. Solvers work by trying to minimize the infeasibility in the model (residual of the constraints), which generally means they push any infeasibility onto the least sensitive constraint in the problem. Thus, the constraint which shows the infeasibility is often not the root cause of the problem, but only the symptom of the underlying issue.\n",
- "\n",
- "If we look back at the constraints, we can see that the same variables also appear in ``c3`` and that some of these have bounds, all of which could be contributing to the infeasibility. In this case the solver tried to minimize the residual in all the constraints and ended up pushing all the issues off onto ``c2``.\n",
- "\n",
- "\n",
- "Warning:\n",
- "When dealing with solver issues such as this, you should always remember that the obvious symptoms are often just the tip of the iceberg and that the real issue generally lies somewhere else; the challenge is tracing the symptoms back to their ultimate source.\n",
- "
\n",
- "\n",
- "Next, let us take a look at the variables at or outside their bounds as well. When a solver reports an potentially infeasible solution, the most common cause is unexpected bounds violations so you should always check these first.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Display the variables with bounds violations.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 29,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Display the variables with bounds violations"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 30,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "The following variable(s) have values at or outside their bounds (tol=0.0E+00):\n",
- "\n",
- " v3 (free): value=0.0 bounds=(0, 5)\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.display_variables_at_or_outside_bounds()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The toolbox is telling us that ``v3`` is the variable with a potential issue. It is also showing us the current value and bounds for ``v3`` as well as if it is a fixed or free variable, which will be useful for diagnosing the issues.\n",
- "\n",
- "We can see that ``v3`` is a free variable with bounds between 0 and 5 and a current value of 0. As ``v3`` is a free variable, this suggests that the solver has pushed the value to the bound where it cannot go any further, and this might be part of the cause of our infeasibility.\n",
- "\n",
- "\n",
- "Warning:\n",
- "When dealing with bounds violations you should always start by understanding why the bounds exist and what they mean - in many cases a bound indicates the range over which the model can be trusted and that going beyond this may result in unexpected behavior due to extrapolation.\n",
- " \n",
- "Never arbitrarily change a bound just because it is causing your model to be infeasible without understanding the consequences of this decision. Often, a bound violation is an indication that you need to re-think some of the constraints in your model to find alternatives which are valid in the actual range of values you are trying to solve for.\n",
- "
\n",
- "\n",
- "For this example, let us assume that we made a mistake with the bounds on ``v3`` and set the lower bound to be -5.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Update the bounds on v3 in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 31,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Update bounds for v3"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 32,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "m.v3.setlb(-5)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Now that we have fixed the bounds issues, we should check whether our model is now feasible. However, before we continue we should recognize that we have just made a structural change to the model. If we were not careful, this could have introduced new structural issues to the model, so we should start from the beginning just to be sure.\n",
- "\n",
- "\n",
- "Warning:\n",
- "In general, you should always start from the beginning of the model diagnosis workflow after you make any change to the model. Remember to also record these changes in your log book in case something unexpected happens so that you can revert any changes that cause problems.\n",
- "
\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Check to see if there are any new structural issues in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 33,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Check for new strucutral issues"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 34,
- "metadata": {
- "scrolled": true,
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Model Statistics\n",
- "\n",
- " Activated Blocks: 1 (Deactivated: 0)\n",
- " Free Variables in Activated Constraints: 4 (External: 0)\n",
- " Free Variables with only lower bounds: 0\n",
- " Free Variables with only upper bounds: 0\n",
- " Free Variables with upper and lower bounds: 2\n",
- " Fixed Variables in Activated Constraints: 3 (External: 0)\n",
- " Activated Equality Constraints: 4 (Deactivated: 0)\n",
- " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
- " Activated Objectives: 0 (Deactivated: 0)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "0 WARNINGS\n",
- "\n",
- " No warnings found!\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "2 Cautions\n",
- "\n",
- " Caution: 1 variable fixed to 0\n",
- " Caution: 1 unused variable (0 fixed)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "Suggested next steps:\n",
- "\n",
- " Try to initialize/solve your model and then call report_numerical_issues()\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.report_structural_issues()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Our change has not introduced any new structural issues, so we can move on and try to solve the model again.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Re-solve the model in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 35,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Re-solve the model"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 36,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "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...: 7\n",
- "Number of nonzeros in inequality constraint Jacobian.: 0\n",
- "Number of nonzeros in Lagrangian Hessian.............: 0\n",
- "\n",
- "Total number of variables............................: 4\n",
- " variables with only lower bounds: 0\n",
- " variables with lower and upper bounds: 2\n",
- " variables with only upper bounds: 0\n",
- "Total number of equality constraints.................: 4\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 6.67e-01 0.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
- " 1 0.0000000e+00 6.66e-03 2.97e+00 -1.0 2.00e+00 - 7.17e-01 9.90e-01h 1\n",
- " 2 0.0000000e+00 6.27e-05 9.38e+00 -1.0 2.00e-02 - 1.00e+00 9.91e-01h 1\n",
- " 3 0.0000000e+00 8.88e-16 1.13e-12 -1.0 1.88e-04 - 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....: 8.8817841970012523e-16 8.8817841970012523e-16\n",
- "Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
- "Overall NLP error.......: 8.8817841970012523e-16 8.8817841970012523e-16\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"
- ]
- },
- {
- "data": {
- "text/plain": [
- "{'Problem': [{'Lower bound': -inf, 'Upper bound': inf, 'Number of objectives': 1, 'Number of constraints': 4, 'Number of variables': 4, 'Sense': 'unknown'}], 'Solver': [{'Status': 'ok', 'Message': 'Ipopt 3.13.2\\\\x3a Optimal Solution Found', 'Termination condition': 'optimal', 'Id': 0, 'Error rc': 0, 'Time': 0.02317023277282715}], 'Solution': [OrderedDict([('number of solutions', 0), ('number of solutions displayed', 0)])]}"
- ]
- },
- "execution_count": 36,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "solver.solve(m, tee=True)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "IPOPT should have returned optimal solution now, so it looks like those bounds were what was causing the model to be infeasible. At this point, the model is now solving (for the current values at least), so you might think that the model is now ready for optimization.\n",
- "\n",
- "However, if we look at the solver logs we can see that it took around 3 iterations for IPOPT to solve our model (depending on minor variations in computer architecture). For a model this simple, we would generally expect it to solve in only 1 iteration so there is still some room for improvement.\n",
- "\n",
- "\n",
- "Warning:\n",
- "You should keep in mind that just because you get an optimal solution does not mean that your model is robust and free of issues.\n",
- " \n",
- "You should always take the time to look over the solver logs to look for signs of trouble, even if you get an optimal solution. While you might get an optimal solution for the current state, there may be advance warning signs of issues that will cause problems later when you try to solve the model at a different state.\n",
- "
\n",
- "\n",
- "Let us run the ``report_numerical_issues`` method again to see if there are any other problems we need to address.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Check for additional numerical issues in the cell below.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 37,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Check for additional numerical issues"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 38,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Model Statistics\n",
- "\n",
- " Jacobian Condition Number: 1.700E+01\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "0 WARNINGS\n",
- "\n",
- " No warnings found!\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "5 Cautions\n",
- "\n",
- " Caution: 1 Variable with value close to their bounds (abs=1.0E-04, rel=1.0E-04)\n",
- " Caution: 1 Variable with value close to zero (tol=1.0E-08)\n",
- " Caution: 1 Variable with extreme value (<1.0E-04 or >1.0E+04)\n",
- " Caution: 1 Variable with None value\n",
- " Caution: 1 extreme Jacobian Entry (<1.0E-04 or >1.0E+04)\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "Suggested next steps:\n",
- "\n",
- " If you still have issues converging your model consider:\n",
- " prepare_svd_toolbox()\n",
- " prepare_degeneracy_hunter()\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.report_numerical_issues()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The toolbox is not reporting any warnings which is good, however there are still 5 numerical cautions that it has identified which might be contributing to the larger than expected number of iterations. As mentioned earlier, the toolbox does not suggest methods for investigating these, but there are methods available. For example, we can look at the variable with an extreme value using the `display_variables_with_extreme_values()` method.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Check for additional information about variables with extreme values.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 39,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Display variable with extreme value"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 40,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "The following variable(s) have extreme values (<1.0E-04 or > 1.0E+04):\n",
- "\n",
- " v7: 4.9999999999999945e-08\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt.display_variables_with_extreme_values()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We can see that ``v7`` is potentially causing problems due to having a very small value (on the order of magnitude of the solver tolerance). This can be especially problematic for interior point solvers like IPOPT if there is a lower bound of 0 (which there is in this case). IPOPT tries to avoid bounds and thus perturbs solutions away from these if it gets too close, which can cause convergence to be slow (or fail) if the solution lies close to the bound.\n",
- "\n",
- "We can address this by scaling the variable so that the value of the scaled variable is large enough that the solution is not close to the lower bound. Additionally, we should look at any constraint that ``v7`` appears in (in this case ``c4``) and ensure that those constraints are well scaled as well (so that a residual of 1e-6 is reasonable for the terms involved).\n",
- "\n",
- "For this case, we can set a scaling factor of 1e8 for both ``v7`` and ``c4`` as shown below. Note that we also need to apply Pyomo's scaling transformation to create a new scaled model to work with."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 41,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.scaling_factor = pyo.Suffix(direction=pyo.Suffix.EXPORT)\n",
- "\n",
- "m.scaling_factor[m.v7] = 1e8\n",
- "m.scaling_factor[m.c4] = 1e8\n",
- "\n",
- "scaling = pyo.TransformationFactory('core.scale_model')\n",
- "scaled_model = scaling.create_using(m, rename=False)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Now that we have a scaled model, we can try to solve it and hopefully see better convergence than the unscaled model.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Solve the scaled model and check to see how many iterations are required.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 42,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Solve scaled model"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 43,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "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...: 7\n",
- "Number of nonzeros in inequality constraint Jacobian.: 0\n",
- "Number of nonzeros in Lagrangian Hessian.............: 0\n",
- "\n",
- "Total number of variables............................: 4\n",
- " variables with only lower bounds: 0\n",
- " variables with lower and upper bounds: 2\n",
- " variables with only upper bounds: 0\n",
- "Total number of equality constraints.................: 4\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 5.33e-15 0.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
- "\n",
- "Number of Iterations....: 0\n",
- "\n",
- " (scaled) (unscaled)\n",
- "Objective...............: 0.0000000000000000e+00 0.0000000000000000e+00\n",
- "Dual infeasibility......: 0.0000000000000000e+00 0.0000000000000000e+00\n",
- "Constraint violation....: 5.3290705182007514e-15 5.3290705182007514e-15\n",
- "Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
- "Overall NLP error.......: 5.3290705182007514e-15 5.3290705182007514e-15\n",
- "\n",
- "\n",
- "Number of objective function evaluations = 1\n",
- "Number of objective gradient evaluations = 1\n",
- "Number of equality constraint evaluations = 1\n",
- "Number of inequality constraint evaluations = 0\n",
- "Number of equality constraint Jacobian evaluations = 1\n",
- "Number of inequality constraint Jacobian evaluations = 0\n",
- "Number of Lagrangian Hessian evaluations = 0\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"
- ]
- },
- {
- "data": {
- "text/plain": [
- "{'Problem': [{'Lower bound': -inf, 'Upper bound': inf, 'Number of objectives': 1, 'Number of constraints': 4, 'Number of variables': 4, 'Sense': 'unknown'}], 'Solver': [{'Status': 'ok', 'Message': 'Ipopt 3.13.2\\\\x3a Optimal Solution Found', 'Termination condition': 'optimal', 'Id': 0, 'Error rc': 0, 'Time': 0.0058002471923828125}], 'Solution': [OrderedDict([('number of solutions', 0), ('number of solutions displayed', 0)])]}"
- ]
- },
- "execution_count": 43,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "solver.solve(scaled_model, tee=True)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "As we can see, the scaled model solved in 0 iterations (indicating that it already had the right solution). However, had we done this to the unscaled model we would have found it required 2-3 iterations again due to IPOPT perturbing the initial (correct) solution away from the bounds.\n",
- "\n",
- "\n",
- "Warning:\n",
- "Normally in these cases we would need to map the solution from the scaled model back to the unscaled model so we can view the results. In this case, we are not actually interested in the solution so we move on with the model diagnosis.\n",
- "
\n",
- "\n",
- "Now that we have fixed the scaling issues, we can go back to the ``DiagnosticsToolbox`` and see if we still have any warnings. Note however that we need to look at the scaled model now rather than the original model, so we need to create a new instance of the ``DiagnositcsToolbox`` with the scaled model as the ``model`` argument.\n",
- "\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Create a new instance of the DiagnosticsToolbox and check the scaled model for issues.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 45,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Create a new diagnostics toolbox for scaled model\n",
- "\n",
- "# Report numerical issues for scaled model\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 46,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "====================================================================================\n",
- "Model Statistics\n",
- "\n",
- " Jacobian Condition Number: 1.800E+01\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "0 WARNINGS\n",
- "\n",
- " No warnings found!\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "3 Cautions\n",
- "\n",
- " Caution: 1 Variable with value close to their bounds (abs=1.0E-04, rel=1.0E-04)\n",
- " Caution: 1 Variable with value close to zero (tol=1.0E-08)\n",
- " Caution: 1 Variable with None value\n",
- "\n",
- "------------------------------------------------------------------------------------\n",
- "Suggested next steps:\n",
- "\n",
- " If you still have issues converging your model consider:\n",
- " prepare_svd_toolbox()\n",
- " prepare_degeneracy_hunter()\n",
- "\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "dt_scaled = DiagnosticsToolbox(scaled_model)\n",
- "dt_scaled.report_numerical_issues()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We can see that applying scaling addressed two of the cautions we had before (the variable with an extreme value and an associated large value in the model Jacobian). Whilst we were able to solve the unscaled model in this case, this is in part because it was a simple linear model. In more complex, non-linear models, scaling becomes much more important and often depends strongly on the current state of the model. That is, you can often find cases where the unscaled (or poorly scaled) model solves for a limited range of conditions but fails to solve if you move too far away for the current state. Whilst you might be able to solve the model at the current state, you should always check the solver logs and numerical cautions for advanced warning signs of scaling issues that might manifest later when you try to solve the model for a different state (e.g., during optimization).\n",
- "\n",
- "\n",
- "Warning:\n",
- "By their nature, numerical issues depend on the current values of the variables in the model, and thus may remain hidden until someone tries to solve the model close to where the issue exists. For this reason, the full model diagnostics workflow contains steps to run the numerical checks across a wide range of variable values to try to ensure that no issues remain hidden. This is beyond the scope of this tutorial however.\n",
- "
\n",
- "\n",
- "At this point, we have addressed all the issues that were preventing us from solving the demonstration model and so reached the end of this tutorial. For cases where we are still having trouble solving the model, we can see that the toolbox is suggesting additional methods for further debugging and these advanced features will be the focus of separate tutorials."
- ]
+ "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": [
+ "# IDAES Model Diagnostics Toolbox Tutorial\n",
+ "Author: Andrew Lee \n",
+ "Maintainer: Andrew Lee \n",
+ "Updated: 2023-10-31 \n",
+ "\n",
+ "As you have likely discovered already, developing and solving models in an equation-oriented (EO) environment can be challenging and often takes a significant amount of effort. There are many pitfalls and mistakes that can be encountered when developing a model which can greatly impact the solvability and robustness of the final problem.\n",
+ "\n",
+ "Model diagnosis and debugging is often more of an art than a science, and it generally relies on significant experience and understanding both of general EO modeling techniques and the specific model and problem being solved. To assist with this process, IDAES has developed a model diagnostics toolbox that brings together a large number of tools for identifying potential issues in a model to help guide the user through the process of finding and resolving these issues. Note however that whilst these tools can help identify the presence of an issue, remedying the issue always requires some degree of engineering knowledge about the system being modeled, and thus it is ultimately up to the user to find a solution to the problem.\n",
+ "\n",
+ "This tutorial will take you through using the {py:class}`DiagnosticsToolbox ` to debug a number of issues in a simple Pyomo model and to take it from initially reporting a possible infeasible solution to returning the correct solution.\n",
+ "\n",
+ "To get started, the ``DiagnosticsToolbox`` can be imported from ``idaes.core.util``.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Import the DiagnosticsToolbox in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from idaes.core.util import DiagnosticsToolbox"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "To get some information on where to start, try using the Python ``help()`` function to see the documentation for the ``DiagnosticsToolbox``.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Call `help(DiagnosticsToolbox)` to see some more information on the toolbox and some instructions on how to get started.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Call the help() function for more information"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Help on class DiagnosticsToolbox in module idaes.core.util.model_diagnostics:\n",
+ "\n",
+ "class DiagnosticsToolbox(builtins.object)\n",
+ " | DiagnosticsToolbox(model: pyomo.core.base.block._BlockData, **kwargs)\n",
+ " | \n",
+ " | The IDAES Model DiagnosticsToolbox.\n",
+ " | \n",
+ " | To get started:\n",
+ " | \n",
+ " | 1. Create an instance of your model (this does not need to be initialized yet).\n",
+ " | 2. Fix variables until you have 0 degrees of freedom. Many of these tools presume\n",
+ " | a square model, and a square model should always be the foundation of any more\n",
+ " | advanced model.\n",
+ " | 3. Create an instance of the DiagnosticsToolbox and provide the model to debug as\n",
+ " | the model argument.\n",
+ " | 4. Call the ``report_structural_issues()`` method.\n",
+ " | \n",
+ " | Model diagnostics is an iterative process and you will likely need to run these\n",
+ " | tools multiple times to resolve all issues. After making a change to your model,\n",
+ " | you should always start from the beginning again to ensure the change did not\n",
+ " | introduce any new issues; i.e., always start from the report_structural_issues()\n",
+ " | method.\n",
+ " | \n",
+ " | Note that structural checks do not require the model to be initialized, thus users\n",
+ " | should start with these. Numerical checks require at least a partial solution to the\n",
+ " | model and should only be run once all structural issues have been resolved.\n",
+ " | \n",
+ " | Report methods will print a summary containing three parts:\n",
+ " | \n",
+ " | 1. Warnings - these are critical issues that should be resolved before continuing.\n",
+ " | For each warning, a method will be suggested in the Next Steps section to get\n",
+ " | additional information.\n",
+ " | 2. Cautions - these are things that could be correct but could also be the source of\n",
+ " | solver issues. Not all cautions need to be addressed, but users should investigate\n",
+ " | each one to ensure that the behavior is correct and that they will not be the source\n",
+ " | of difficulties later. Methods exist to provide more information on all cautions,\n",
+ " | but these will not appear in the Next Steps section.\n",
+ " | 3. Next Steps - these are recommended methods to call from the DiagnosticsToolbox to\n",
+ " | get further information on warnings. If no warnings are found, this will suggest\n",
+ " | the next report method to call.\n",
+ " | \n",
+ " | Args:\n",
+ " | \n",
+ " | model: model to be diagnosed. The DiagnosticsToolbox does not support indexed Blocks.\n",
+ " | \n",
+ " | Keyword Arguments\n",
+ " | -----------------\n",
+ " | variable_bounds_absolute_tolerance: float, default=0.0001\n",
+ " | Absolute tolerance for considering a variable to be close to its\n",
+ " | bounds.\n",
+ " | \n",
+ " | variable_bounds_relative_tolerance: float, default=0.0001\n",
+ " | Relative tolerance for considering a variable to be close to its\n",
+ " | bounds.\n",
+ " | \n",
+ " | variable_bounds_violation_tolerance: float, default=0\n",
+ " | Absolute tolerance for considering a variable to violate its bounds.\n",
+ " | Some solvers relax bounds on variables thus allowing a small violation\n",
+ " | to be considered acceptable.\n",
+ " | \n",
+ " | constraint_residual_tolerance: float, default=1e-05\n",
+ " | Absolute tolerance to use when checking constraint residuals.\n",
+ " | \n",
+ " | variable_large_value_tolerance: float, default=10000.0\n",
+ " | Absolute tolerance for considering a value to be large.\n",
+ " | \n",
+ " | variable_small_value_tolerance: float, default=0.0001\n",
+ " | Absolute tolerance for considering a value to be small.\n",
+ " | \n",
+ " | variable_zero_value_tolerance: float, default=1e-08\n",
+ " | Absolute tolerance for considering a value to be near to zero.\n",
+ " | \n",
+ " | jacobian_large_value_caution: float, default=10000.0\n",
+ " | Tolerance for raising a caution for large Jacobian values.\n",
+ " | \n",
+ " | jacobian_large_value_warning: float, default=100000000.0\n",
+ " | Tolerance for raising a warning for large Jacobian values.\n",
+ " | \n",
+ " | jacobian_small_value_caution: float, default=0.0001\n",
+ " | Tolerance for raising a caution for small Jacobian values.\n",
+ " | \n",
+ " | jacobian_small_value_warning: float, default=1e-08\n",
+ " | Tolerance for raising a warning for small Jacobian values.\n",
+ " | \n",
+ " | warn_for_evaluation_error_at_bounds: bool, default=True\n",
+ " | If False, warnings will not be generated for things like log(x) with x\n",
+ " | >= 0\n",
+ " | \n",
+ " | Methods defined here:\n",
+ " | \n",
+ " | __init__(self, model: pyomo.core.base.block._BlockData, **kwargs)\n",
+ " | Initialize self. See help(type(self)) for accurate signature.\n",
+ " | \n",
+ " | assert_no_numerical_warnings(self)\n",
+ " | Checks for numerical warnings in the model and raises an AssertionError\n",
+ " | if any are found.\n",
+ " | \n",
+ " | Raises:\n",
+ " | AssertionError if any warnings are identified by numerical analysis.\n",
+ " | \n",
+ " | assert_no_structural_warnings(self)\n",
+ " | Checks for structural warnings in the model and raises an AssertionError\n",
+ " | if any are found.\n",
+ " | \n",
+ " | Raises:\n",
+ " | AssertionError if any warnings are identified by structural analysis.\n",
+ " | \n",
+ " | display_components_with_inconsistent_units(self, stream=None)\n",
+ " | Prints a list of all Constraints, Expressions and Objectives in the\n",
+ " | model with inconsistent units of measurement.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_constraints_with_extreme_jacobians(self, stream=None)\n",
+ " | Prints the constraints associated with rows in the Jacobian with extreme\n",
+ " | L2 norms. This often indicates poorly scaled constraints.\n",
+ " | \n",
+ " | Tolerances can be set via the DiagnosticsToolbox config.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the output to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_constraints_with_large_residuals(self, stream=None)\n",
+ " | Prints a list of Constraints with residuals greater than a specified tolerance.\n",
+ " | Tolerance can be set in the class configuration options.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_external_variables(self, stream=None)\n",
+ " | Prints a list of variables that appear within activated Constraints in the\n",
+ " | model but are not contained within the model themselves.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_extreme_jacobian_entries(self, stream=None)\n",
+ " | Prints variables and constraints associated with entries in the Jacobian with extreme\n",
+ " | values. This can be indicative of poor scaling, especially for isolated terms (e.g.\n",
+ " | variables which appear only in one term of a single constraint).\n",
+ " | \n",
+ " | Tolerances can be set via the DiagnosticsToolbox config.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the output to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_overconstrained_set(self, stream=None)\n",
+ " | Prints the variables and constraints in the over-constrained sub-problem\n",
+ " | from a Dulmage-Mendelsohn partitioning.\n",
+ " | \n",
+ " | This can be used to identify the over-defined part of a model and thus\n",
+ " | where constraints must be removed or variables unfixed.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_potential_evaluation_errors(self, stream=None)\n",
+ " | Prints constraints that may be prone to evaluation errors\n",
+ " | (e.g., log of a negative number) based on variable bounds.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the output to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_underconstrained_set(self, stream=None)\n",
+ " | Prints the variables and constraints in the under-constrained sub-problem\n",
+ " | from a Dulmage-Mendelsohn partitioning.\n",
+ " | \n",
+ " | This can be used to identify the under-defined part of a model and thus\n",
+ " | where additional information (fixed variables or constraints) are required.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_unused_variables(self, stream=None)\n",
+ " | Prints a list of variables that do not appear in any activated Constraints.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_variables_at_or_outside_bounds(self, stream=None)\n",
+ " | Prints a list of variables with values that fall at or outside the bounds\n",
+ " | on the variable.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_variables_fixed_to_zero(self, stream=None)\n",
+ " | Prints a list of variables that are fixed to an absolute value of 0.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_variables_near_bounds(self, stream=None)\n",
+ " | Prints a list of variables with values close to their bounds. Tolerance can\n",
+ " | be set in the class configuration options.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_variables_with_extreme_jacobians(self, stream=None)\n",
+ " | Prints the variables associated with columns in the Jacobian with extreme\n",
+ " | L2 norms. This often indicates poorly scaled variables.\n",
+ " | \n",
+ " | Tolerances can be set via the DiagnosticsToolbox config.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the output to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_variables_with_extreme_values(self, stream=None)\n",
+ " | Prints a list of variables with extreme values.\n",
+ " | \n",
+ " | Tolerances can be set in the class configuration options.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_variables_with_none_value(self, stream=None)\n",
+ " | Prints a list of variables with a value of None.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | display_variables_with_value_near_zero(self, stream=None)\n",
+ " | Prints a list of variables with a value close to zero. The tolerance\n",
+ " | for determining what is close to zero can be set in the class configuration\n",
+ " | options.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: an I/O object to write the list to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | get_dulmage_mendelsohn_partition(self)\n",
+ " | Performs a Dulmage-Mendelsohn partitioning on the model and returns\n",
+ " | the over- and under-constrained sub-problems.\n",
+ " | \n",
+ " | Returns:\n",
+ " | list-of-lists variables in each independent block of the under-constrained set\n",
+ " | list-of-lists constraints in each independent block of the under-constrained set\n",
+ " | list-of-lists variables in each independent block of the over-constrained set\n",
+ " | list-of-lists constraints in each independent block of the over-constrained set\n",
+ " | \n",
+ " | prepare_degeneracy_hunter(self, **kwargs)\n",
+ " | Create an instance of the DegeneracyHunter and store as self.degeneracy_hunter.\n",
+ " | \n",
+ " | After creating an instance of the toolbox, call\n",
+ " | report_irreducible_degenerate_sets.\n",
+ " | \n",
+ " | Returns:\n",
+ " | \n",
+ " | Instance of DegeneracyHunter\n",
+ " | \n",
+ " | Keyword Arguments\n",
+ " | -----------------\n",
+ " | solver: str, default='scip'\n",
+ " | MILP solver to use for finding irreducible degenerate sets.\n",
+ " | \n",
+ " | solver_options: optional\n",
+ " | Options to pass to MILP solver.\n",
+ " | \n",
+ " | M: float, default=100000.0\n",
+ " | Maximum value for nu in MILP models.\n",
+ " | \n",
+ " | m_small: float, default=1e-05\n",
+ " | Smallest value for nu to be considered non-zero in MILP models.\n",
+ " | \n",
+ " | trivial_constraint_tolerance: float, default=1e-06\n",
+ " | Tolerance for identifying non-zero rows in Jacobian.\n",
+ " | \n",
+ " | prepare_svd_toolbox(self, **kwargs)\n",
+ " | Create an instance of the SVDToolbox and store as self.svd_toolbox.\n",
+ " | \n",
+ " | After creating an instance of the toolbox, call\n",
+ " | display_underdetermined_variables_and_constraints().\n",
+ " | \n",
+ " | Returns:\n",
+ " | \n",
+ " | Instance of SVDToolbox\n",
+ " | \n",
+ " | Keyword Arguments\n",
+ " | -----------------\n",
+ " | number_of_smallest_singular_values: PositiveInt, optional\n",
+ " | Number of smallest singular values to compute\n",
+ " | \n",
+ " | svd_callback: svd_callback_validator, default=\n",
+ " | Callback to SVD method of choice (default = svd_dense). Callbacks\n",
+ " | should take the Jacobian and number of singular values to compute as\n",
+ " | options, plus any method specific arguments, and should return the u,\n",
+ " | s and v matrices as numpy arrays.\n",
+ " | \n",
+ " | svd_callback_arguments: dict, optional\n",
+ " | Optional arguments to pass to SVD callback (default = None)\n",
+ " | \n",
+ " | singular_value_tolerance: float, default=1e-06\n",
+ " | Tolerance for defining a small singular value\n",
+ " | \n",
+ " | size_cutoff_in_singular_vector: float, default=0.1\n",
+ " | Size below which to ignore constraints and variables in the singular\n",
+ " | vector\n",
+ " | \n",
+ " | report_numerical_issues(self, stream=None)\n",
+ " | Generates a summary report of any numerical issues identified in the model provided\n",
+ " | and suggest next steps for debugging model.\n",
+ " | \n",
+ " | Numerical checks should only be performed once all structural issues have been resolved,\n",
+ " | and require that at least a partial solution to the model is available.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: I/O object to write report to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | report_structural_issues(self, stream=None)\n",
+ " | Generates a summary report of any structural issues identified in the model provided\n",
+ " | and suggests next steps for debugging the model.\n",
+ " | \n",
+ " | This should be the first method called when debugging a model and after any change\n",
+ " | is made to the model. These checks can be run before trying to initialize and solve\n",
+ " | the model.\n",
+ " | \n",
+ " | Args:\n",
+ " | stream: I/O object to write report to (default = stdout)\n",
+ " | \n",
+ " | Returns:\n",
+ " | None\n",
+ " | \n",
+ " | ----------------------------------------------------------------------\n",
+ " | Readonly properties defined here:\n",
+ " | \n",
+ " | model\n",
+ " | Model currently being diagnosed.\n",
+ " | \n",
+ " | ----------------------------------------------------------------------\n",
+ " | Data descriptors defined here:\n",
+ " | \n",
+ " | __dict__\n",
+ " | dictionary for instance variables (if defined)\n",
+ " | \n",
+ " | __weakref__\n",
+ " | list of weak references to the object (if defined)\n",
+ "\n"
+ ]
}
- ],
- "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"
+ ],
+ "source": [
+ "help(DiagnosticsToolbox)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The ``help()`` function gives us a lot of information on the ``DiagnosticsToolbox`` and all the methods that it supports (and there are many). However, the important part to start with are the four steps outlined at the top of the doc string that tell us how to get started.\n",
+ "\n",
+ "Firstly, we need a model to test (and, for this tutorial at least, one that has a wide range of issues that we need to fix before it will solve). We then also need to fix some variables so that we have 0 degrees of freedom in our model. Whilst our ultimate goal is generally optimization (and thus a system with 1 or more degrees of freedom), all models conceptually derive from a square model representing a nominal state. If this nominal state is not well-posed, then any issues present will also be present in the resulting optimization (even if adding degrees of freedom means that the model is now easier to solve).\n",
+ "\n",
+ "The cell below contains a demonstration model for this tutorial that contains a number of issues that we will resolve using the ``DiagnosticsToolbox``."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import pyomo.environ as pyo\n",
+ "\n",
+ "m = pyo.ConcreteModel()\n",
+ "\n",
+ "m.v1 = pyo.Var(units=pyo.units.m)\n",
+ "m.v2 = pyo.Var(units=pyo.units.m)\n",
+ "m.v3 = pyo.Var(bounds=(0, 5))\n",
+ "m.v4 = pyo.Var()\n",
+ "m.v5 = pyo.Var(bounds=(0, 10))\n",
+ "m.v6 = pyo.Var()\n",
+ "m.v7 = pyo.Var(\n",
+ " units=pyo.units.m, bounds=(0, 1)\n",
+ ") # Poorly scaled variable with lower bound\n",
+ "m.v8 = pyo.Var() # unused variable\n",
+ "\n",
+ "m.c1 = pyo.Constraint(expr=m.v1 + m.v2 == 10) # Unit consistency issue\n",
+ "m.c2 = pyo.Constraint(expr=m.v3 == m.v4 + m.v5)\n",
+ "m.c3 = pyo.Constraint(expr=2 * m.v3 == 3 * m.v4 + 4 * m.v5 + m.v6)\n",
+ "m.c4 = pyo.Constraint(expr=m.v7 == 1e-8 * m.v1) # Poorly scaled constraint\n",
+ "\n",
+ "m.v4.fix(2)\n",
+ "m.v5.fix(2)\n",
+ "m.v6.fix(0)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Next, the instructions tell us to create an instance of the ``DiagnosticsToolbox`` and to pass the model we wish to examine as an argument.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Create an instance of the DiagnosticsToolbox: dt = DiagnosticsToolbox(m)\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Create an instance of the Diagnostics Toolbox"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "dt = DiagnosticsToolbox(m)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Finally, the instructions tell us to run the ``report_structural_issues()`` method. Structural issues represent issues that exist solely in the form of the model equations and thus do not depend on the current value of any of the variables. This is useful as it means we can check for these before we even call a solver, which can be critical as sometimes these issues will cause a solver to fail without providing a useful solution.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Call dt.report_structural_issues() in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Call the report_structural_issues() method"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Activated Blocks: 1 (Deactivated: 0)\n",
+ " Free Variables in Activated Constraints: 4 (External: 0)\n",
+ " Free Variables with only lower bounds: 0\n",
+ " Free Variables with only upper bounds: 0\n",
+ " Free Variables with upper and lower bounds: 2\n",
+ " Fixed Variables in Activated Constraints: 3 (External: 0)\n",
+ " Activated Equality Constraints: 4 (Deactivated: 0)\n",
+ " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
+ " Activated Objectives: 0 (Deactivated: 0)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 WARNINGS\n",
+ "\n",
+ " WARNING: 1 Component with inconsistent units\n",
+ " WARNING: Structural singularity found\n",
+ " Under-Constrained Set: 3 variables, 2 constraints\n",
+ " Over-Constrained Set: 1 variables, 2 constraints\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 Cautions\n",
+ "\n",
+ " Caution: 1 variable fixed to 0\n",
+ " Caution: 1 unused variable (0 fixed)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " display_components_with_inconsistent_units()\n",
+ " display_underconstrained_set()\n",
+ " display_overconstrained_set()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Looking at the output from the ``report_structural_issues()`` method, we can see that it provides a fairly short summary containing 4 sections.\n",
+ "\n",
+ "1. The first section is a summary of the size of the model, indicating things like the number of variables and constraints. The size of the model is often important for judging how difficult it will be to solve, and this information can also be useful for comparison to what is being sent to the solver. Most solvers will report the size of the model in their output logs, and if there is a difference between what is reported here and by the solver, then you should probably look into what is happening. This section also notes some things such as if you have any deactivated Blocks, Constraints or Objectives, or if you have variables which appear in the constraints that are not part of the model; these are not necessarily wrong but it is easy to have accidentally deactivated something you did not intend to so you should always check to see that these are expected.\n",
+ "\n",
+ "2. The second section provides a summary of any critical structural issues that were found - in this case we can see that there are 2 warnings we are going to need to look into. Warnings represent issues that need to be addressed before moving on as these will likely cause the solver to fail or give an incorrect answer.\n",
+ "\n",
+ "3. The third section lists a summary of any cautions that are found. Cautions represent issues that may or may not be problematic; in many cases these might be expected behaviors or borderline issues. However, these could also represent conceptual issues that should be addressed, so users should take the time to investigate these and determine if they need to be fixed or not.\n",
+ "\n",
+ "4. Finally, there is a section that suggests the next steps to take to help guide you through the model diagnosis process. If any warnings were identified, this section will list methods that can help you get more information on each specific problem, and if no warnings are found then it will guide you onto the next step in the model diagnosis workflow.\n",
+ "\n",
+ "**Note:** there are methods available to help investigate cautions as well, but these will not show up in the next steps in order to avoid cluttering the output. You can get more information on the available methods for investigating cautions via the documentation or ``help()`` function.\n",
+ "\n",
+ "In our current model, we have 2 critical issues (warnings) that we need to look into and resolve. The order in which we resolve these will generally not matter, but be aware that these can often be interrelated - fixing one warning might resolve other warnings as well (or create new ones), and sometimes you will need to look at multiple issues together to find the overall root cause.\n",
+ "\n",
+ "To start with, let us look at the unit consistency issue. From the \"Next Steps\" section above, the toolbox is suggesting we run the ``display_components_with_inconsistent_units()`` method for more information.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Call the `display_components_with_inconsistent_units()` method from the DiagnosticsToolbox to see more information on which constraint is causing the unit consistency issues.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Call the display_components_with_inconsistent_units() method"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {
+ "scrolled": false,
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "The following component(s) have unit consistency issues:\n",
+ "\n",
+ " c1\n",
+ "\n",
+ "For more details on unit inconsistencies, import the assert_units_consistent method\n",
+ "from pyomo.util.check_units\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.display_components_with_inconsistent_units()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This tells us that the issue lies in constraint ``c1``. If we go back and look at this constraint, we can see that it says ``v1 + v2 == 10``. ``v1`` and ``v2`` both have units of ``m`` which is consistent, but the constant in the expression (right hand side) is unitless. Thus, we need to correct this so that the right hand side has units for the constraint to be consistent.\n",
+ "\n",
+ "The cell below shows how to delete a constraint and replace it with a new one with the correct units.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "Deleting components can cause unexpected issues if something else in a model is using that component (e.g., deleting a variable which is used in a constraint). You should always be careful when deleting Pyomo components and make sure you only delete components that are not used elsewhere.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Delete the incorrect Constraint\n",
+ "m.del_component(m.c1)\n",
+ "\n",
+ "# Re-create the Constraint with the correct units\n",
+ "m.c1 = pyo.Constraint(expr=m.v1 + m.v2 == 10 * pyo.units.m)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Warning:\n",
+ "Fixing issues in models is often an iterative process requiring trial and error. You might also have some results from a model before running the diagnostics tools and the changes you make during debugging may make it difficult to replicate those results afterwards.\n",
+ " \n",
+ "It is strongly recommended that you keep a record of the changes you make at each step and why, along with a Git hash (or similar version control marker) corresponding to these changes. This will allow you see what changes and why, and give you a way to go back to previous iterations if the current approach does not work out. The IDAES documentation contains recommendations on how to keep and maintain a modeling logbook.\n",
+ "
\n",
+ "\n",
+ "Now, re-run the ``report_structural_issues()`` method and see if this change has fixed the unit consistency issue.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Call dt.report_structural_issues() in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Call the report_structural_issues() method"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {
+ "scrolled": true,
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Activated Blocks: 1 (Deactivated: 0)\n",
+ " Free Variables in Activated Constraints: 4 (External: 0)\n",
+ " Free Variables with only lower bounds: 0\n",
+ " Free Variables with only upper bounds: 0\n",
+ " Free Variables with upper and lower bounds: 2\n",
+ " Fixed Variables in Activated Constraints: 3 (External: 0)\n",
+ " Activated Equality Constraints: 4 (Deactivated: 0)\n",
+ " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
+ " Activated Objectives: 0 (Deactivated: 0)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "1 WARNINGS\n",
+ "\n",
+ " WARNING: Structural singularity found\n",
+ " Under-Constrained Set: 3 variables, 2 constraints\n",
+ " Over-Constrained Set: 1 variables, 2 constraints\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 Cautions\n",
+ "\n",
+ " Caution: 1 variable fixed to 0\n",
+ " Caution: 1 unused variable (0 fixed)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " display_underconstrained_set()\n",
+ " display_overconstrained_set()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The unit consistency issue has been resolved by the changes above, so now we need to look at the structural singularity. A structural singularity occurs when one sub-part of the model is over-constrained (negative degrees of freedom), which generally means another part is under-constrained (positive degrees of freedom, assuming that there are 0 degrees of freedom overall).\n",
+ "\n",
+ "The toolbox is suggesting we use the ``display_overconstrained_set()`` and ``display_underconstrained_set()`` methods to get more information on the singularity; for now, let us start with the over-constrained set.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Call dt.display_overconstrained_set() in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Call the display_overconstrained_set() method"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Dulmage-Mendelsohn Over-Constrained Set\n",
+ "\n",
+ " Independent Block 0:\n",
+ "\n",
+ " Variables:\n",
+ "\n",
+ " v3\n",
+ "\n",
+ " Constraints:\n",
+ "\n",
+ " c2\n",
+ " c3\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.display_overconstrained_set()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "From the output above, the toolbox is telling us that we have two constraints (``c2`` and ``c3``) which only contain a single unfixed variable (``v3``); thus in this part of the model we have -1 degree of freedom and the model is not well defined (structurally singular). If we go back and look at these constraints, we can see the that the constraints are:\n",
+ "\n",
+ "``c2: v3 == v4 + v5``\n",
+ "\n",
+ "``c3: 2*v3 == 3*v4 + 4*v5 + v6``\n",
+ "\n",
+ "We can see that in addition to ``v3`` these constraints actually contain 3 other variables (``v4``, ``v5`` and ``v6``), however these are all variables we fixed to get our initial zero degrees of freedom. It looks like we have either accidentally fixed one too many variables or written one too many constraints.\n",
+ "\n",
+ "For this example, let us assume that ``v4`` was not supposed to be fixed and unfix it.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Resolve the structural singularity and then call dt.report_structural_issues() in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Unfix v4\n",
+ "\n",
+ "# Then call the report_structural_issues() method again"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "metadata": {
+ "scrolled": true,
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Activated Blocks: 1 (Deactivated: 0)\n",
+ " Free Variables in Activated Constraints: 5 (External: 0)\n",
+ " Free Variables with only lower bounds: 0\n",
+ " Free Variables with only upper bounds: 0\n",
+ " Free Variables with upper and lower bounds: 2\n",
+ " Fixed Variables in Activated Constraints: 2 (External: 0)\n",
+ " Activated Equality Constraints: 4 (Deactivated: 0)\n",
+ " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
+ " Activated Objectives: 0 (Deactivated: 0)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 WARNINGS\n",
+ "\n",
+ " WARNING: 1 Degree of Freedom\n",
+ " WARNING: Structural singularity found\n",
+ " Under-Constrained Set: 3 variables, 2 constraints\n",
+ " Over-Constrained Set: 0 variables, 0 constraints\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 Cautions\n",
+ "\n",
+ " Caution: 1 variable fixed to 0\n",
+ " Caution: 1 unused variable (0 fixed)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " display_underconstrained_set()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
}
+ ],
+ "source": [
+ "m.v4.unfix()\n",
+ "\n",
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We can see that the over-constrained set is now empty (0 variables and 0 constraints) but the under-constrained set still has 3 variables and only 2 constraints. We can also see that there is a new warning about having 1 degree of freedom in the model, however this should not be surprising as we have just unfixed ``v4`` to resolve the over-constrained set so we have added a degree of freedom to the model.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Display the under-constrained set in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Display the under-constrained set"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 20,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Dulmage-Mendelsohn Under-Constrained Set\n",
+ "\n",
+ " Independent Block 0:\n",
+ "\n",
+ " Variables:\n",
+ "\n",
+ " v2\n",
+ " v1\n",
+ " v7\n",
+ "\n",
+ " Constraints:\n",
+ "\n",
+ " c1\n",
+ " c4\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.display_underconstrained_set()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Looking at the output from the ``display_underconstrained_set()`` method, we can see that we have two constraints, ``c1`` and ``c4``, which contain three unfixed variables, ``v1``, ``v2`` and ``v7``. Thus, we have one degree of freedom that needs to be addressed. To fix this, we could either fix one of the variables shown or add an additional equality constraint to the model.\n",
+ "\n",
+ "For this example let's fix ``v2`` to a value of 5 and then re-run the ``report_structural_issues()`` method.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Fix v2 to a value of 5 and then re-run dt.report_structural_issues.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Fix v2 = 5\n",
+ "\n",
+ "# Then re-run report_structural_issues() method"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "metadata": {
+ "scrolled": true,
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Activated Blocks: 1 (Deactivated: 0)\n",
+ " Free Variables in Activated Constraints: 4 (External: 0)\n",
+ " Free Variables with only lower bounds: 0\n",
+ " Free Variables with only upper bounds: 0\n",
+ " Free Variables with upper and lower bounds: 2\n",
+ " Fixed Variables in Activated Constraints: 3 (External: 0)\n",
+ " Activated Equality Constraints: 4 (Deactivated: 0)\n",
+ " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
+ " Activated Objectives: 0 (Deactivated: 0)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "0 WARNINGS\n",
+ "\n",
+ " No warnings found!\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 Cautions\n",
+ "\n",
+ " Caution: 1 variable fixed to 0\n",
+ " Caution: 1 unused variable (0 fixed)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " Try to initialize/solve your model and then call report_numerical_issues()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "m.v2.fix(5)\n",
+ "\n",
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The toolbox is now telling us that no warnings were found, so we have resolved all the structural issues (for now at least). The toolbox is telling us that there are also 2 non-critical issues (cautions) that we should look at; one about an unused variable and one about a variable fixed to zero. If you wish, you can look into identifying and fixing these yourself, however for this example we will move on to the next step (remember that the toolbox has methods to display more details for each of these which you can find in the documentation or from the ``help()`` function).\n",
+ "\n",
+ "For the Next Steps section, the toolbox is recommending we try to solve our model and then check for numerical issues.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Use the Pyomo SolverFactory to create an instance of IPOPT and then try to solve the model. Make sure to set \"tee=True\" as this is going to fail (and it is always good practice to review the solver logs).\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 23,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Create a solver object\n",
+ "\n",
+ "# Try to solve the model"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "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...: 7\n",
+ "Number of nonzeros in inequality constraint Jacobian.: 0\n",
+ "Number of nonzeros in Lagrangian Hessian.............: 0\n",
+ "\n",
+ "Total number of variables............................: 4\n",
+ " variables with only lower bounds: 0\n",
+ " variables with lower and upper bounds: 2\n",
+ " variables with only upper bounds: 0\n",
+ "Total number of equality constraints.................: 4\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 1.40e+01 0.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
+ " 1 0.0000000e+00 1.39e+01 1.50e+02 -1.0 6.00e+00 - 7.16e-01 4.93e-03h 1\n",
+ " 2 0.0000000e+00 1.39e+01 3.03e+06 -1.0 5.97e+00 - 1.00e+00 4.95e-05h 1\n",
+ " 3r 0.0000000e+00 1.39e+01 1.00e+03 1.1 0.00e+00 - 0.00e+00 2.47e-07R 2\n",
+ " 4r 0.0000000e+00 4.19e+00 9.42e+02 1.1 3.50e+03 - 4.02e-01 3.37e-03f 1\n",
+ " 5r 0.0000000e+00 2.12e+00 8.72e+02 1.1 5.89e+01 - 4.35e-01 7.06e-02f 1\n",
+ " 6r 0.0000000e+00 6.74e-01 6.06e+02 1.1 5.29e+00 - 9.93e-03 3.98e-01f 1\n",
+ " 7r 0.0000000e+00 6.80e-01 3.14e+02 0.4 2.05e-01 - 1.00e+00 1.03e-01f 1\n",
+ " 8r 0.0000000e+00 6.69e-01 2.78e-05 0.4 2.58e-02 - 1.00e+00 1.00e+00f 1\n",
+ " 9r 0.0000000e+00 6.67e-01 7.56e+00 -1.7 8.13e-03 - 9.93e-01 9.96e-01f 1\n",
+ "iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n",
+ " 10r 0.0000000e+00 6.67e-01 2.23e-07 -1.7 4.13e-05 - 1.00e+00 1.00e+00f 1\n",
+ " 11r 0.0000000e+00 6.67e-01 6.73e-01 -3.7 6.61e-05 - 1.00e+00 1.00e+00f 1\n",
+ " 12r 0.0000000e+00 6.67e-01 1.91e-09 -3.7 1.48e-09 - 1.00e+00 1.00e+00h 1\n",
+ " 13r 0.0000000e+00 6.67e-01 2.69e+00 -8.4 5.74e-07 - 1.00e+00 9.26e-01f 1\n",
+ " 14r 0.0000000e+00 6.67e-01 7.65e+01 -8.4 4.23e-08 - 8.68e-01 1.00e+00f 1\n",
+ "\n",
+ "Number of Iterations....: 14\n",
+ "\n",
+ " (scaled) (unscaled)\n",
+ "Objective...............: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Dual infeasibility......: 3.2644919411246030e-04 3.2644919411246030e-04\n",
+ "Constraint violation....: 6.6666666333656233e-01 6.6666666333656233e-01\n",
+ "Complementarity.........: 4.6615546565561981e-09 4.6615546565561981e-09\n",
+ "Overall NLP error.......: 6.6666666333656233e-01 6.6666666333656233e-01\n",
+ "\n",
+ "\n",
+ "Number of objective function evaluations = 18\n",
+ "Number of objective gradient evaluations = 5\n",
+ "Number of equality constraint evaluations = 18\n",
+ "Number of inequality constraint evaluations = 0\n",
+ "Number of equality constraint Jacobian evaluations = 17\n",
+ "Number of inequality constraint Jacobian evaluations = 0\n",
+ "Number of Lagrangian Hessian evaluations = 15\n",
+ "Total CPU secs in IPOPT (w/o function evaluations) = 0.003\n",
+ "Total CPU secs in NLP function evaluations = 0.000\n",
+ "\n",
+ "EXIT: Converged to a point of local infeasibility. Problem may be infeasible.\n",
+ "WARNING: Loading a SolverResults object with a warning status into\n",
+ "model.name=\"unknown\";\n",
+ " - termination condition: infeasible\n",
+ " - message from solver: Ipopt 3.13.2\\x3a Converged to a locally infeasible\n",
+ " point. Problem may be infeasible.\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "{'Problem': [{'Lower bound': -inf, 'Upper bound': inf, 'Number of objectives': 1, 'Number of constraints': 4, 'Number of variables': 4, 'Sense': 'unknown'}], 'Solver': [{'Status': 'warning', 'Message': 'Ipopt 3.13.2\\\\x3a Converged to a locally infeasible point. Problem may be infeasible.', 'Termination condition': 'infeasible', 'Id': 200, 'Error rc': 0, 'Time': 0.007064104080200195}], 'Solution': [OrderedDict([('number of solutions', 0), ('number of solutions displayed', 0)])]}"
+ ]
+ },
+ "execution_count": 24,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "solver = pyo.SolverFactory(\"ipopt\")\n",
+ "solver.solve(m, tee=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As hinted at above, IPOPT has returned a warning that the problem may be infeasible. Before moving on however, it is always good practice to look over the solver outputs and see what it is telling you.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "A lot of useful information is contained in the solver logs which is extremely useful when diagnosing modeling issues. Each solver has its own way of reporting output and its own specific behavior, so you will need to learn to interpret the output of each solver you use. The IDAES Documentation contains some guidance on interpreting output logs for a few common solvers.\n",
+ "
\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Call the report_numerical_issues method in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Check for numerical issues"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 26,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Jacobian Condition Number: 1.700E+01\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 WARNINGS\n",
+ "\n",
+ " WARNING: 1 Constraint with large residuals (>1.0E-05)\n",
+ " WARNING: 1 Variable at or outside bounds (tol=0.0E+00)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "5 Cautions\n",
+ "\n",
+ " Caution: 2 Variables with value close to their bounds (abs=1.0E-04, rel=1.0E-04)\n",
+ " Caution: 2 Variables with value close to zero (tol=1.0E-08)\n",
+ " Caution: 1 Variable with extreme value (<1.0E-04 or >1.0E+04)\n",
+ " Caution: 1 Variable with None value\n",
+ " Caution: 1 extreme Jacobian Entry (<1.0E-04 or >1.0E+04)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " display_constraints_with_large_residuals()\n",
+ " display_variables_at_or_outside_bounds()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.report_numerical_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The ``report_numerical_issues()`` provides a summary similar to that which we saw for the structural issues. Firstly, it reports to us the Jacobian condition number for our problem which can give us an idea of how well-scaled the problem is, followed by a list of warnings, cautions and suggested next steps.\n",
+ "\n",
+ "Unsurprisingly, we are seeing a warning about a constraint with a large residual which we would expect when a solver reports a potentially infeasible problem. We are also seeing a warning about a variable with bound violations which might be contributing to the potential infeasibility.\n",
+ "\n",
+ "For the next steps, the toolbox is suggesting some new methods to get more information on these issues; let us start by looking at the constraints with large residuals.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Display the constraint with a large residual in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 27,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Display constraint with large residual"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 28,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "The following constraint(s) have large residuals (>1.0E-05):\n",
+ "\n",
+ " c2: 6.66667E-01\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.display_constraints_with_large_residuals()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The toolbox is telling us that the constraint which failed to converge is ``c2``, however this is generally only part of the story. Solvers work by trying to minimize the infeasibility in the model (residual of the constraints), which generally means they push any infeasibility onto the least sensitive constraint in the problem. Thus, the constraint which shows the infeasibility is often not the root cause of the problem, but only the symptom of the underlying issue.\n",
+ "\n",
+ "If we look back at the constraints, we can see that the same variables also appear in ``c3`` and that some of these have bounds, all of which could be contributing to the infeasibility. In this case the solver tried to minimize the residual in all the constraints and ended up pushing all the issues off onto ``c2``.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "When dealing with solver issues such as this, you should always remember that the obvious symptoms are often just the tip of the iceberg and that the real issue generally lies somewhere else; the challenge is tracing the symptoms back to their ultimate source.\n",
+ "
\n",
+ "\n",
+ "Next, let us take a look at the variables at or outside their bounds as well. When a solver reports an potentially infeasible solution, the most common cause is unexpected bounds violations so you should always check these first.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Display the variables with bounds violations.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 29,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Display the variables with bounds violations"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 30,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "The following variable(s) have values at or outside their bounds (tol=0.0E+00):\n",
+ "\n",
+ " v3 (free): value=0.0 bounds=(0, 5)\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.display_variables_at_or_outside_bounds()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The toolbox is telling us that ``v3`` is the variable with a potential issue. It is also showing us the current value and bounds for ``v3`` as well as if it is a fixed or free variable, which will be useful for diagnosing the issues.\n",
+ "\n",
+ "We can see that ``v3`` is a free variable with bounds between 0 and 5 and a current value of 0. As ``v3`` is a free variable, this suggests that the solver has pushed the value to the bound where it cannot go any further, and this might be part of the cause of our infeasibility.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "When dealing with bounds violations you should always start by understanding why the bounds exist and what they mean - in many cases a bound indicates the range over which the model can be trusted and that going beyond this may result in unexpected behavior due to extrapolation.\n",
+ " \n",
+ "Never arbitrarily change a bound just because it is causing your model to be infeasible without understanding the consequences of this decision. Often, a bound violation is an indication that you need to re-think some of the constraints in your model to find alternatives which are valid in the actual range of values you are trying to solve for.\n",
+ "
\n",
+ "\n",
+ "For this example, let us assume that we made a mistake with the bounds on ``v3`` and set the lower bound to be -5.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Update the bounds on v3 in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 31,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Update bounds for v3"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 32,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "m.v3.setlb(-5)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now that we have fixed the bounds issues, we should check whether our model is now feasible. However, before we continue we should recognize that we have just made a structural change to the model. If we were not careful, this could have introduced new structural issues to the model, so we should start from the beginning just to be sure.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "In general, you should always start from the beginning of the model diagnosis workflow after you make any change to the model. Remember to also record these changes in your log book in case something unexpected happens so that you can revert any changes that cause problems.\n",
+ "
\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Check to see if there are any new structural issues in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 33,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Check for new structural issues"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 34,
+ "metadata": {
+ "scrolled": true,
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Activated Blocks: 1 (Deactivated: 0)\n",
+ " Free Variables in Activated Constraints: 4 (External: 0)\n",
+ " Free Variables with only lower bounds: 0\n",
+ " Free Variables with only upper bounds: 0\n",
+ " Free Variables with upper and lower bounds: 2\n",
+ " Fixed Variables in Activated Constraints: 3 (External: 0)\n",
+ " Activated Equality Constraints: 4 (Deactivated: 0)\n",
+ " Activated Inequality Constraints: 0 (Deactivated: 0)\n",
+ " Activated Objectives: 0 (Deactivated: 0)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "0 WARNINGS\n",
+ "\n",
+ " No warnings found!\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "2 Cautions\n",
+ "\n",
+ " Caution: 1 variable fixed to 0\n",
+ " Caution: 1 unused variable (0 fixed)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " Try to initialize/solve your model and then call report_numerical_issues()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Our change has not introduced any new structural issues, so we can move on and try to solve the model again.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Re-solve the model in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 35,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Re-solve the model"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 36,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "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...: 7\n",
+ "Number of nonzeros in inequality constraint Jacobian.: 0\n",
+ "Number of nonzeros in Lagrangian Hessian.............: 0\n",
+ "\n",
+ "Total number of variables............................: 4\n",
+ " variables with only lower bounds: 0\n",
+ " variables with lower and upper bounds: 2\n",
+ " variables with only upper bounds: 0\n",
+ "Total number of equality constraints.................: 4\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 6.67e-01 0.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
+ " 1 0.0000000e+00 6.66e-03 2.97e+00 -1.0 2.00e+00 - 7.17e-01 9.90e-01h 1\n",
+ " 2 0.0000000e+00 6.27e-05 9.38e+00 -1.0 2.00e-02 - 1.00e+00 9.91e-01h 1\n",
+ " 3 0.0000000e+00 8.88e-16 1.13e-12 -1.0 1.88e-04 - 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....: 8.8817841970012523e-16 8.8817841970012523e-16\n",
+ "Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Overall NLP error.......: 8.8817841970012523e-16 8.8817841970012523e-16\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"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "{'Problem': [{'Lower bound': -inf, 'Upper bound': inf, 'Number of objectives': 1, 'Number of constraints': 4, 'Number of variables': 4, 'Sense': 'unknown'}], 'Solver': [{'Status': 'ok', 'Message': 'Ipopt 3.13.2\\\\x3a Optimal Solution Found', 'Termination condition': 'optimal', 'Id': 0, 'Error rc': 0, 'Time': 0.02317023277282715}], 'Solution': [OrderedDict([('number of solutions', 0), ('number of solutions displayed', 0)])]}"
+ ]
+ },
+ "execution_count": 36,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "solver.solve(m, tee=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "IPOPT should have returned optimal solution now, so it looks like those bounds were what was causing the model to be infeasible. At this point, the model is now solving (for the current values at least), so you might think that the model is now ready for optimization.\n",
+ "\n",
+ "However, if we look at the solver logs we can see that it took around 3 iterations for IPOPT to solve our model (depending on minor variations in computer architecture). For a model this simple, we would generally expect it to solve in only 1 iteration so there is still some room for improvement.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "You should keep in mind that just because you get an optimal solution does not mean that your model is robust and free of issues.\n",
+ " \n",
+ "You should always take the time to look over the solver logs to look for signs of trouble, even if you get an optimal solution. While you might get an optimal solution for the current state, there may be advance warning signs of issues that will cause problems later when you try to solve the model at a different state.\n",
+ "
\n",
+ "\n",
+ "Let us run the ``report_numerical_issues`` method again to see if there are any other problems we need to address.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Check for additional numerical issues in the cell below.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 37,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Check for additional numerical issues"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 38,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Jacobian Condition Number: 1.700E+01\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "0 WARNINGS\n",
+ "\n",
+ " No warnings found!\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "5 Cautions\n",
+ "\n",
+ " Caution: 1 Variable with value close to their bounds (abs=1.0E-04, rel=1.0E-04)\n",
+ " Caution: 1 Variable with value close to zero (tol=1.0E-08)\n",
+ " Caution: 1 Variable with extreme value (<1.0E-04 or >1.0E+04)\n",
+ " Caution: 1 Variable with None value\n",
+ " Caution: 1 extreme Jacobian Entry (<1.0E-04 or >1.0E+04)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " If you still have issues converging your model consider:\n",
+ " prepare_svd_toolbox()\n",
+ " prepare_degeneracy_hunter()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.report_numerical_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The toolbox is not reporting any warnings which is good, however there are still 5 numerical cautions that it has identified which might be contributing to the larger than expected number of iterations. As mentioned earlier, the toolbox does not suggest methods for investigating these, but there are methods available. For example, we can look at the variable with an extreme value using the `display_variables_with_extreme_values()` method.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Check for additional information about variables with extreme values.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 39,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Display variable with extreme value"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 40,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "The following variable(s) have extreme values (<1.0E-04 or > 1.0E+04):\n",
+ "\n",
+ " v7: 4.9999999999999945e-08\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt.display_variables_with_extreme_values()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We can see that ``v7`` is potentially causing problems due to having a very small value (on the order of magnitude of the solver tolerance). This can be especially problematic for interior point solvers like IPOPT if there is a lower bound of 0 (which there is in this case). IPOPT tries to avoid bounds and thus perturbs solutions away from these if it gets too close, which can cause convergence to be slow (or fail) if the solution lies close to the bound.\n",
+ "\n",
+ "We can address this by scaling the variable so that the value of the scaled variable is large enough that the solution is not close to the lower bound. Additionally, we should look at any constraint that ``v7`` appears in (in this case ``c4``) and ensure that those constraints are well scaled as well (so that a residual of 1e-6 is reasonable for the terms involved).\n",
+ "\n",
+ "For this case, we can set a scaling factor of 1e8 for both ``v7`` and ``c4`` as shown below. Note that we also need to apply Pyomo's scaling transformation to create a new scaled model to work with."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 41,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.scaling_factor = pyo.Suffix(direction=pyo.Suffix.EXPORT)\n",
+ "\n",
+ "m.scaling_factor[m.v7] = 1e8\n",
+ "m.scaling_factor[m.c4] = 1e8\n",
+ "\n",
+ "scaling = pyo.TransformationFactory(\"core.scale_model\")\n",
+ "scaled_model = scaling.create_using(m, rename=False)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now that we have a scaled model, we can try to solve it and hopefully see better convergence than the unscaled model.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Solve the scaled model and check to see how many iterations are required.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 42,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Solve scaled model"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 43,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "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...: 7\n",
+ "Number of nonzeros in inequality constraint Jacobian.: 0\n",
+ "Number of nonzeros in Lagrangian Hessian.............: 0\n",
+ "\n",
+ "Total number of variables............................: 4\n",
+ " variables with only lower bounds: 0\n",
+ " variables with lower and upper bounds: 2\n",
+ " variables with only upper bounds: 0\n",
+ "Total number of equality constraints.................: 4\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 5.33e-15 0.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
+ "\n",
+ "Number of Iterations....: 0\n",
+ "\n",
+ " (scaled) (unscaled)\n",
+ "Objective...............: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Dual infeasibility......: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Constraint violation....: 5.3290705182007514e-15 5.3290705182007514e-15\n",
+ "Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Overall NLP error.......: 5.3290705182007514e-15 5.3290705182007514e-15\n",
+ "\n",
+ "\n",
+ "Number of objective function evaluations = 1\n",
+ "Number of objective gradient evaluations = 1\n",
+ "Number of equality constraint evaluations = 1\n",
+ "Number of inequality constraint evaluations = 0\n",
+ "Number of equality constraint Jacobian evaluations = 1\n",
+ "Number of inequality constraint Jacobian evaluations = 0\n",
+ "Number of Lagrangian Hessian evaluations = 0\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"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "{'Problem': [{'Lower bound': -inf, 'Upper bound': inf, 'Number of objectives': 1, 'Number of constraints': 4, 'Number of variables': 4, 'Sense': 'unknown'}], 'Solver': [{'Status': 'ok', 'Message': 'Ipopt 3.13.2\\\\x3a Optimal Solution Found', 'Termination condition': 'optimal', 'Id': 0, 'Error rc': 0, 'Time': 0.0058002471923828125}], 'Solution': [OrderedDict([('number of solutions', 0), ('number of solutions displayed', 0)])]}"
+ ]
+ },
+ "execution_count": 43,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "solver.solve(scaled_model, tee=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As we can see, the scaled model solved in 0 iterations (indicating that it already had the right solution). However, had we done this to the unscaled model we would have found it required 2-3 iterations again due to IPOPT perturbing the initial (correct) solution away from the bounds.\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "Normally in these cases we would need to map the solution from the scaled model back to the unscaled model so we can view the results. In this case, we are not actually interested in the solution so we move on with the model diagnosis.\n",
+ "
\n",
+ "\n",
+ "Now that we have fixed the scaling issues, we can go back to the ``DiagnosticsToolbox`` and see if we still have any warnings. Note however that we need to look at the scaled model now rather than the original model, so we need to create a new instance of the ``DiagnosticsToolbox`` with the scaled model as the ``model`` argument.\n",
+ "\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Create a new instance of the DiagnosticsToolbox and check the scaled model for issues.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 45,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Create a new diagnostics toolbox for scaled model\n",
+ "\n",
+ "# Report numerical issues for scaled model"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 46,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "====================================================================================\n",
+ "Model Statistics\n",
+ "\n",
+ " Jacobian Condition Number: 1.800E+01\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "0 WARNINGS\n",
+ "\n",
+ " No warnings found!\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "3 Cautions\n",
+ "\n",
+ " Caution: 1 Variable with value close to their bounds (abs=1.0E-04, rel=1.0E-04)\n",
+ " Caution: 1 Variable with value close to zero (tol=1.0E-08)\n",
+ " Caution: 1 Variable with None value\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ "Suggested next steps:\n",
+ "\n",
+ " If you still have issues converging your model consider:\n",
+ " prepare_svd_toolbox()\n",
+ " prepare_degeneracy_hunter()\n",
+ "\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "dt_scaled = DiagnosticsToolbox(scaled_model)\n",
+ "dt_scaled.report_numerical_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We can see that applying scaling addressed two of the cautions we had before (the variable with an extreme value and an associated large value in the model Jacobian). Whilst we were able to solve the unscaled model in this case, this is in part because it was a simple linear model. In more complex, non-linear models, scaling becomes much more important and often depends strongly on the current state of the model. That is, you can often find cases where the unscaled (or poorly scaled) model solves for a limited range of conditions but fails to solve if you move too far away for the current state. Whilst you might be able to solve the model at the current state, you should always check the solver logs and numerical cautions for advanced warning signs of scaling issues that might manifest later when you try to solve the model for a different state (e.g., during optimization).\n",
+ "\n",
+ "\n",
+ "Warning:\n",
+ "By their nature, numerical issues depend on the current values of the variables in the model, and thus may remain hidden until someone tries to solve the model close to where the issue exists. For this reason, the full model diagnostics workflow contains steps to run the numerical checks across a wide range of variable values to try to ensure that no issues remain hidden. This is beyond the scope of this tutorial however.\n",
+ "
\n",
+ "\n",
+ "At this point, we have addressed all the issues that were preventing us from solving the demonstration model and so reached the end of this tutorial. For cases where we are still having trouble solving the model, we can see that the toolbox is suggesting additional methods for further debugging and these advanced features will be the focus of separate tutorials."
+ ]
+ }
+ ],
+ "metadata": {
+ "celltoolbar": "Tags",
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
},
- "nbformat": 4,
- "nbformat_minor": 3
+ "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
}
\ No newline at end of file
diff --git a/idaes_examples/notebooks/docs/diagnostics/index.md b/idaes_examples/notebooks/docs/diagnostics/index.md
index b38fb2fb..4defbd31 100644
--- a/idaes_examples/notebooks/docs/diagnostics/index.md
+++ b/idaes_examples/notebooks/docs/diagnostics/index.md
@@ -3,3 +3,4 @@
IDAES tools for diagnostics:
* Introduction to the Diagnostics Toolbox
* Solver degeneracy debugging (Degeneracy Hunter)
+* Debugging a structural singularity
diff --git a/idaes_examples/notebooks/docs/diagnostics/structural_singularity.ipynb b/idaes_examples/notebooks/docs/diagnostics/structural_singularity.ipynb
new file mode 100644
index 00000000..6142a0e1
--- /dev/null
+++ b/idaes_examples/notebooks/docs/diagnostics/structural_singularity.ipynb
@@ -0,0 +1,757 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "a261df3e-3957-4a85-ac6d-1bbe9eb053f8",
+ "metadata": {},
+ "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",
+ "id": "86cdef66",
+ "metadata": {},
+ "source": [
+ "Debugging a Structural Singularity\n",
+ "===========================\n",
+ "Author: Robert Parker\\\n",
+ "Maintainer: Robert Parker\\\n",
+ "Updated: 2024-06-10\n",
+ "\n",
+ "In this tutorial, we will use the [IDAES Diagnostics Toolbox](https://idaes-pse.readthedocs.io/en/2.4.0/explanations/model_diagnostics/index.html#diagnostics-toolbox)\n",
+ "to diagnose and fix a structural singularity that is preventing us from solving an optimization problem."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "f456b3f7",
+ "metadata": {
+ "jp-MarkdownHeadingCollapsed": true
+ },
+ "source": [
+ "# Constructing the model\n",
+ "\n",
+ "Suppose a collaborator has given us a model to work with. They give us a square model and tell us what the degrees of freedom are. We construct an optimization problem and try to solve it. In this tutorial, we don't want to worry too much about the details that go into constructing the model. This has been provided in the `idaes_examples.mod.diagnostics.gas_solid_contactors.model` module.\n",
+ "\n",
+ "## Model details (OKAY TO SKIP)\n",
+ "\n",
+ "The model we are trying to optimize is a dynamic model of a moving bed chemical looping combustion reactor. The model has been described by [Okoli et al.][1] and [Parker and Biegler][2]. This is a gas-solid reactor with counter-current flow. The degrees of freedom are gas and solid inlet flow rates, and we are trying to minimize the deviation from a desired operating point via a least-squares objective function.\n",
+ "\n",
+ "[1]: https://www.sciencedirect.com/science/article/pii/S0032591019302803\n",
+ "[2]: https://www.sciencedirect.com/science/article/pii/S2405896322008825\n",
+ "\n",
+ "Again, we don't want to worry too much about the model. The `make_model` function will construct the optimization problem that we want to solve, and whenever we do something model-specific, we will explicitly make note of it."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c276aa4a",
+ "metadata": {},
+ "source": [
+ "# Trying to solve the original model\n",
+ "\n",
+ "With that out of the way, let's construct the model and try to solve it!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "201f63f2",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from idaes_examples.mod.diagnostics.gas_solid_contactors.model import make_model\n",
+ "import logging\n",
+ "\n",
+ "# We'll turn off IDAES logging. This is not recommended in general, but this is an old model\n",
+ "# (from IDAES 1.7) that has been ported to work with the current version of IDAES. It generates\n",
+ "# a lot of warnings.\n",
+ "logging.getLogger(\"idaes\").setLevel(logging.CRITICAL)\n",
+ "# We'll also turn off Pyomo logging. This will suppress unit inconsistency warnings later,\n",
+ "# which otherwise flood our console and slow down this notebook. We have unit inconsistencies\n",
+ "# as, in IDAES 1.7, we didn't rigorously enforce that models use units.\n",
+ "logging.getLogger(\"pyomo\").setLevel(logging.CRITICAL)\n",
+ "\n",
+ "# This constructs a dynamic model with degrees of freedom and an objective function.\n",
+ "model = make_model()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "2cac2868",
+ "metadata": {},
+ "source": [
+ "Before trying to solve the model, let's make sure it conforms to our expectations, i.e. it (a) has degrees of freedom and (b) is well-initialized to a feasible point."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "8133d68f",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Import some useful utilities from the model_statistics module.\n",
+ "# Degrees of freedom and constraint residuals are always good things to check before\n",
+ "# trying to solve a simulation or optimization problem.\n",
+ "from idaes.core.util.model_statistics import degrees_of_freedom, large_residuals_set\n",
+ "\n",
+ "dof = degrees_of_freedom(model)\n",
+ "print(f\"Degrees of freedom: {dof}\")\n",
+ "has_large_residuals = bool(large_residuals_set(model, tol=1e-5))\n",
+ "print(f\"Has large residuals: {has_large_residuals}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "86e1a118",
+ "metadata": {},
+ "source": [
+ "In the above `make_model` function, the model has been \"solved\" to arrive at a feasible point, then degrees of freedom have been unfixed and an objective function has been added to give us an optimization problem. This looks good so far, so let's try to solve the optimization problem."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "7b8ddf55",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Import pyomo.environ for access to solvers\n",
+ "import pyomo.environ as pyo\n",
+ "\n",
+ "solver = pyo.SolverFactory(\"ipopt\")\n",
+ "solver.options[\"max_iter\"] = 20\n",
+ "solver.options[\"print_user_options\"] = \"yes\"\n",
+ "solver.options[\"OF_print_info_string\"] = \"yes\"\n",
+ "res = solver.solve(model, tee=True)\n",
+ "print(f\"Converged successfully: {pyo.check_optimal_termination(res)}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "d3513d46",
+ "metadata": {},
+ "source": [
+ "IPOPT fails to solve the optimization problem... You can try increasing the iteration limit, but it is very unlikely that this model will ever solve. A telltale sign that something is wrong with our model is the persistence of regularization coefficients, that is, numbers in the `lg(rg)` column of the IPOPT log. These coefficients can have multiple causes. One is that the constraint Jacobian (partial derivative matrix) is singular, which indicates a problem with our model. We have set the `print_info_string` option in IPOPT to display \"diagnostic tags\" to help interpret these regularization coefficients. The \"L\" and \"l\" diagnostic tags, which appear repeatedly, indicate that the Jacobian is singular. For more information on IPOPT diagnostic tags, see the IPOPT [documentation](https://coin-or.github.io/Ipopt/OUTPUT.html)."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b01c2de5",
+ "metadata": {},
+ "source": [
+ "# Debugging the original model\n",
+ "\n",
+ "Let's run the diagnostics toolbox on the model and see what it has to say.\n",
+ "\n",
+ "For good practice, we'll first make sure the model we're debugging is square. Remember that we're assuming we already know how to toggle degrees of freedom in our model."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "107471c0",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Fix gas and solid flow rates at their respective inlets\n",
+ "model.fs.MB.gas_phase.properties[:, 0].flow_mol.fix()\n",
+ "model.fs.MB.solid_phase.properties[:, 1].flow_mass.fix()\n",
+ "# Part of our optimization problem was a set of constraints to enforce piecewise\n",
+ "# constant control inputs. We need to deactivate these as well.\n",
+ "model.piecewise_constant_constraints.deactivate()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "36f82a0a",
+ "metadata": {},
+ "source": [
+ "Now we can run the diagnostics toolbox."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "e7667a9f",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from idaes.core.util.model_diagnostics import DiagnosticsToolbox\n",
+ "\n",
+ "dt = DiagnosticsToolbox(model)\n",
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "3b4530cf",
+ "metadata": {},
+ "source": [
+ "Let's look at the warnings we got:\n",
+ "- Inconsistent units\n",
+ "- Structural singularity\n",
+ "- Potential evaluation errors\n",
+ "\n",
+ "We'll ignore the inconsistent units. The property package and unit model here were extracted from IDAES 1.7, before we rigorously enforced that all models use units. The potential evaluation errors we see here may be worth looking into, but looking at the failing IPOPT log above, we don't notice any evaluation errors. (If evaluation errors occurred in IPOPT, we would see a message like \"Error in AMPL evaluation\" in the IPOPT iteration log, which we don't see here.) The structural singularity looks like the most promising avenue to debug, especially as the IPOPT log displays persistent regularization coefficients that appear to be caused by a singular Jacobian.\n",
+ "\n",
+ "Let's follow the toolbox's advice and display the under and over-constrained sets."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "9976bc61",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "dt.display_underconstrained_set()\n",
+ "dt.display_overconstrained_set()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "eb67e99c",
+ "metadata": {},
+ "source": [
+ "## Over and under-constrained subsystems\n",
+ "\n",
+ "Structural singularities are characterized by the [Dulmage-Mendelson decomposition][3], which partitions a system into minimal over and under-constrained subsystems. These subsystems contain the potentially unmatched constraints and variables, respectively. Here, \"unmatched\" effectively means \"causing a singularity\". [Pothen and Fan][4] give a good overview of the Dulmage-Mendelsohn decomposition and [Parker et al.][5] give several examples.\n",
+ "\n",
+ "[3]: https://www.cambridge.org/core/journals/canadian-journal-of-mathematics/article/coverings-of-bipartite-graphs/413735C5888AB542B92D0C4F402800B1\n",
+ "[4]: https://dl.acm.org/doi/10.1145/98267.98287\n",
+ "[5]: https://www.sciencedirect.com/science/article/pii/S0098135423002533\n",
+ "\n",
+ "The most straightforward way to fix a structural singularity is to fix variables that are in the under-constrained system and deactivate constraints in the over-constrained subsystem. However, this may not be applicable for every model. For example, we may need to add variables and constraints instead. What over and under-constrained subsystems are telling us is that something is wrong with our modeling assumptions. The particular fix that is appropriate will depend heavily on the model.\n",
+ "\n",
+ "If the above output gives us any clues, we can go ahead and start trying to fix things. However, suppose it doesn't. A good strategy is to try to break down the model into smaller, square subsystems that we think should be nonsingular. For a dynamic model like this one, a good candidate is the subsystem of variables and equations at each point in time."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "ea05b00e",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# We've included a utility function to extract the subsystem of variables and equations\n",
+ "# at a specified point in time. If you are dealing with a process flowsheet, here you\n",
+ "# may want to extract each unit model individually.\n",
+ "from idaes_examples.mod.diagnostics.util import get_subsystem_at_time\n",
+ "\n",
+ "# TemporarySubsystemManager is used to temporarily fix some variables to make sure\n",
+ "# we're debugging a square subsystem.\n",
+ "from pyomo.util.subsystems import TemporarySubsystemManager\n",
+ "\n",
+ "# Let's start with t=0. Really, we'd probably want to do this in a loop and try all time points.\n",
+ "t0 = model.fs.time.first()\n",
+ "t_block, inputs = get_subsystem_at_time(model, model.fs.time, t0)\n",
+ "# We'll temporarily fix the \"inputs\" to make sure we have a square system while debugging\n",
+ "with TemporarySubsystemManager(to_fix=inputs):\n",
+ " dt = DiagnosticsToolbox(t_block)\n",
+ " dt.report_structural_issues()\n",
+ " dt.display_underconstrained_set()\n",
+ " dt.display_overconstrained_set()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "986b5113",
+ "metadata": {},
+ "source": [
+ "These over and under-constrained subsystems aren't much smaller, but now the over-constrained system decomposes into 10 small, independent blocks. These should be easier to debug.\n",
+ "\n",
+ "## Debugging the over-constrained subsystem\n",
+ "\n",
+ "To debug the over-constrained subsystem, we look for a constraint that is not calculating any of the variables in the subsystem. The \"odd constraint out\" here seems to be the mass fraction sum, `sum_component_eqn`. This must \"solve for\" one of the mass fractions, which means one of the `material_holdup_calculation` equations must \"solve for\" particle density rather than mass fraction. If we want to see what variables are contained in one of these constraints, we can always `pprint` it:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "4029972c",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model.fs.MB.solid_phase.properties[0, 0.9].sum_component_eqn.pprint()\n",
+ "model.fs.MB.solid_phase.material_holdup_calculation[0, 0.9, \"Sol\", \"Fe3O4\"].pprint()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "a11d6609",
+ "metadata": {},
+ "source": [
+ "If one of these `material_holdup_calculation` equations is solving for particle density, then that means that `density_particle_constraint` is not actually solving for density. Maybe `density_particle_constraint` is over-determining our system?"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "d1836161",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model.fs.MB.solid_phase.properties[0, 0.9].density_particle_constraint.pprint()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c8e9abf7",
+ "metadata": {},
+ "source": [
+ "But this looks like a very reasonable constraint. After some thought, which admittedly requires some knowledge of the process we are modeling, we decide that the right approach is to make particle porosity a variable. We have assumed that porosity is constant, but this overconstrained subsystem is telling us that this assumption is not valid.\n",
+ "\n",
+ "### How did we figure this out? (OKAY TO SKIP)\n",
+ "Adding a variable (including by unfixing a parameter) to an over-constraining constraint will often remove that constraint from the over-constrained subsystem. But how did we know that this was the right thing to do? If you just care about using the diagnostics toolbox to extract as much information about a singularity as possible, you can skip this section. But if you are curious how we determined that particle porosity should not be constant, read on.\n",
+ "\n",
+ "`dens_mass_skeletal` is determined purely by the composition of solid, which is made up of Fe2O3, Fe3O4, and inert Ti2O3. We can view the `density_skeletal_constraint` as follows:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "5c13a3a8-8e77-498d-b6fe-ad1f88413cbf",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model.fs.MB.solid_phase.properties[0, 0.9].density_skeletal_constraint.pprint()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c42000a6-e0f1-4118-b67f-d878bcf777a2",
+ "metadata": {},
+ "source": [
+ "If we assume a constant particle porosity, this gives us a particle porosity that is also uniquely determined by the solid composition by the above `density_particle_constraint`:\n",
+ "```\n",
+ "dens_mass_particle = (1 - porosity) * dens_mass_skeletal\n",
+ "```\n",
+ "But the composition of the solid is determined by the (somewhat misnamed) `material_holdup_calculation` constraints. While the name of these constraints implies they \"calculate holdups,\" material holdups at $t=0$ are fixed as initial conditions (because holdups are the differential variables with respect to time in this model). At other time points, we assume that holdups are specified by differential and discretization equations of the model. This means that the `material_holdup_calculation` constraints actually calculate the solid phase mass fractions *from* the holdups. But as we hinted at above, the 4-by-4 system of holdup calculation constraints, `sum_component_eqn` (which simply constrains the sum of mass fractions to be one), mass fractions, and `dens_mass_particle`, uniquely solve for `dens_mass_particle` *as well as* the mass fractions. But if the holdup variables can be used to solve for the mass fractions, they *also* solve for `dens_mass_skeletal`. So both sides of `density_particle_constraint` are already uniquely determined! This implies that we don't need this constraint at all, but we also know that this constraint has to hold. Something has to give. With this in mind, we actually have several options for how to resolve this overspecification:\n",
+ "1. Remove `density_particle_constraint`. Then we would have `dens_mass_particle` and `dens_mass_skeletal`, with no relationship between them. This would leave us with a mathematically sound model, but with densities that contradict constant particle porosity that we have assumed (which is used elsewhere in the reaction rate calculation equations).\n",
+ "2. Remove the constraints that calculate skeletal density from composition.\n",
+ "3. Relax particle porosity from a parameter to a variable.\n",
+ "\n",
+ "Options 2 and 3 are equally valid. We've chosen option 3, meaning we assume that the particle \"evolves\" with a density that is well determined from its constituent species, rather than changing density to accommodate whatever mass it accumulates via reaction without altering its volume. This exercise should remind us that all mathematical modeling is somewhat of an art. In the process of choosing the \"least bad\" model, it is fairly easy to over or under-specify something by making the wrong combination of assumptions, and the Dulmage-Mendelsohn decomposition is a great tool for detecting when this has happened.\n",
+ "\n",
+ "## Debugging the under-constrained subsystem\n",
+ "\n",
+ "The under-constrained system does not decompose into independent subsystems, making it more difficult to debug. However, by inspection, we notice that the same constraints and variables seem to be repeated at each point in the length domain. For each point in space, the \"odd variable out\" seems to be the total flow rate `flow_mass`. Using some intuition about this particular process model, we may conclude that this variable should be calculated from the solid phase velocity, which is constant. We expect an equation that looks like\n",
+ "```\n",
+ "flow_mass == velocity * area * density\n",
+ "```\n",
+ "\n",
+ "But this equation isn't here... so we need to add it.\n",
+ "\n",
+ "# Fixing the model\n",
+ "\n",
+ "We'll start by creating a fresh copy of the model, so we don't accidentally rely on IPOPT's point of termination."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "01cd1929",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2 = make_model()\n",
+ "# Make the model square while we try to fix the structural singularity\n",
+ "model2.fs.MB.gas_phase.properties[:, 0].flow_mol.fix()\n",
+ "model2.fs.MB.solid_phase.properties[:, 1].flow_mass.fix()\n",
+ "model2.piecewise_constant_constraints.deactivate()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "565599a6",
+ "metadata": {},
+ "source": [
+ "## Adding a new particle porosity variable"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "479f3067",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2.fs.MB.particle_porosity = pyo.Var(\n",
+ " model2.fs.time,\n",
+ " model2.fs.MB.length_domain,\n",
+ " initialize=model2.fs.solid_properties.particle_porosity.value,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b614d7a0",
+ "metadata": {},
+ "source": [
+ "Now we need to replace the old particle porosity parameter with this new variable. Luckily, the old parameter is actually implemented as a fixed variable, so we can easily identify all the constraints it participates in with `IncidenceGraphInterface`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "4d45b4a9",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from pyomo.contrib.incidence_analysis import IncidenceGraphInterface\n",
+ "\n",
+ "igraph = IncidenceGraphInterface(model2, include_fixed=True)\n",
+ "porosity_param = model2.fs.solid_properties.particle_porosity\n",
+ "print(f\"Constraints containing {porosity_param.name}:\")\n",
+ "for con in igraph.get_adjacent_to(porosity_param):\n",
+ " print(f\" {con.name}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "0e6fee8f",
+ "metadata": {},
+ "source": [
+ "Particle porosity only appears in two constraints: the density constraint we saw above, and the reaction rate equation. We can replace particle porosity in these constraints using Pyomo's `replace_expressions` function:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "4d62bd7a",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from pyomo.core.expr import replace_expressions\n",
+ "\n",
+ "for t, x in model2.fs.time * model2.fs.MB.length_domain:\n",
+ " substitution_map = {id(porosity_param): model2.fs.MB.particle_porosity[t, x]}\n",
+ " sp = model2.fs.MB.solid_phase\n",
+ " cons = [\n",
+ " sp.properties[t, x].density_particle_constraint,\n",
+ " sp.reactions[t, x].gen_rate_expression[\"R1\"],\n",
+ " ]\n",
+ " for con in cons:\n",
+ " con.set_value(\n",
+ " replace_expressions(\n",
+ " con.expr, substitution_map, descend_into_named_expressions=True\n",
+ " )\n",
+ " )"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "cff3dc24",
+ "metadata": {},
+ "source": [
+ "We have added a new `particle_porosity` variable, and are using it in the relevant locations. Now we can move on to adding the missing constraint.\n",
+ "\n",
+ "## Adding a new density-flow rate constraint"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "2cd11e5d",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "@model2.fs.MB.Constraint(model2.fs.time, model2.fs.MB.length_domain)\n",
+ "def density_flowrate_constraint(mb, t, x):\n",
+ " return (\n",
+ " mb.velocity_superficial_solid[t]\n",
+ " * mb.bed_area\n",
+ " * mb.solid_phase.properties[t, x].dens_mass_particle\n",
+ " == mb.solid_phase.properties[t, x].flow_mass\n",
+ " )"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "eca5108e",
+ "metadata": {},
+ "source": [
+ "## Testing the new model\n",
+ "\n",
+ "Let's see if these changes have fixed our model."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "dc391ba8",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Construct a new diagnostics toolbox\n",
+ "dt = DiagnosticsToolbox(model2)\n",
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "de070064",
+ "metadata": {},
+ "source": [
+ "The structural singularity seems to be gone! Let's unfix our degrees of freedom and see if we can solve."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "fc14d9e2",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2.fs.MB.gas_phase.properties[:, 0].flow_mol.unfix()\n",
+ "model2.fs.MB.gas_phase.properties[0, 0].flow_mol.fix()\n",
+ "model2.fs.MB.solid_phase.properties[:, 1].flow_mass.unfix()\n",
+ "model2.fs.MB.solid_phase.properties[0, 1].flow_mass.fix()\n",
+ "model2.piecewise_constant_constraints.activate()\n",
+ "\n",
+ "res = solver.solve(model2, tee=True)\n",
+ "print(f\"Converged successfully: {pyo.check_optimal_termination(res)}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "fd8748b9",
+ "metadata": {},
+ "source": [
+ "This doesn't look much better. What's going on? I thought we just fixed the issue?\n",
+ "\n",
+ "# Debugging the model, take two\n",
+ "\n",
+ "Let's check the diagnostics toolbox for numerical issues."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "199e993e",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2.fs.MB.gas_phase.properties[:, 0].flow_mol.fix()\n",
+ "model2.fs.MB.solid_phase.properties[:, 1].flow_mass.fix()\n",
+ "model2.piecewise_constant_constraints.deactivate()\n",
+ "dt.report_numerical_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "a2040b5b",
+ "metadata": {},
+ "source": [
+ "Looks like we have \"parallel constraints\", which are another form of singularity. Let's follow the toolbox's advice to see what they are."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "b8c21fd9-510c-455d-9360-9005710986b6",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "dt.display_near_parallel_constraints()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "e2b8d8d3-344f-496e-9128-0aac83a445d1",
+ "metadata": {},
+ "source": [
+ "`density_flowrate_constraint` is the constraint that we added. What is `solid_super_vel`?"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "f845733e-a315-4833-b0a9-012cc75a3e48",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2.fs.MB.solid_super_vel[0].pprint()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "0559c8c4",
+ "metadata": {},
+ "source": [
+ "This is the same as the constraint we just added! Looks like that constraint already existed at the solid inlet. We can easily deactivate the new constraints at this point in the length domain:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "8f086a50",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2.fs.MB.density_flowrate_constraint[:, 1.0].deactivate();"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "9d257074",
+ "metadata": {},
+ "source": [
+ "But now we have removed constraints from a square model, and expect to have degrees of freedom. Let's see what the diagnostics toolbox has to say."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "c1af8b6c",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "dt = DiagnosticsToolbox(model2)\n",
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "0b27aee5",
+ "metadata": {},
+ "source": [
+ "But this doesn't help us very much. We have some extraneous degrees of freedom, but with 8881 variables in the under-constrained subsystem, it will be difficult to tell what they are. After some thought (and model-specific intuition), we land on the conclusion that maybe we need to fix particle porosity at the solid inlet. Here, total flow rate is specified, and the `solid_super_vel` equation is using it to compute velocity. So we need `dens_mass_particle` to be known, which means we need `particle_porosity` to be fixed."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "656ae921",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2.fs.MB.particle_porosity[:, 1.0].fix();"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "cbb89807",
+ "metadata": {},
+ "source": [
+ "Let's run the diagnostics toolbox as a sanity check."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "6cf86e28",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "dt = DiagnosticsToolbox(model2)\n",
+ "dt.report_structural_issues()\n",
+ "dt.report_numerical_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "016f8799",
+ "metadata": {},
+ "source": [
+ "Looks good! Now we can release our degrees of freedom and try to solve again."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "a2f83e2e",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2.fs.MB.gas_phase.properties[:, 0].flow_mol.unfix()\n",
+ "model2.fs.MB.gas_phase.properties[0, 0].flow_mol.fix()\n",
+ "model2.fs.MB.solid_phase.properties[:, 1].flow_mass.unfix()\n",
+ "model2.fs.MB.solid_phase.properties[0, 1].flow_mass.fix()\n",
+ "model2.piecewise_constant_constraints.activate()\n",
+ "\n",
+ "res = solver.solve(model2, tee=True)\n",
+ "print(f\"Converged successfully: {pyo.check_optimal_termination(res)}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b8587acd",
+ "metadata": {},
+ "source": [
+ "It worked! For the simple optimization problem we have set up, this solve looks a lot more like what we expect.\n",
+ "\n",
+ "# Takeaways from this tutorial\n",
+ "What have we learned?\n",
+ "1. IPOPT using non-zero regularization coefficients hints at a singular Jacobian (especially when \"L\"/\"l\" diagnostic tags are present).\n",
+ "2. When this happens, start by calling `report_structural_issues` to check for a structural singularity. If this looks good, call `report_numerical_issues` to check for a numerical singularity.\n",
+ "3. When debugging a structural singularity, decomposing a problem into subsystems that each should be nonsingular (e.g. unit models or points in time) is very useful.\n",
+ "4. The solution to a structural singularity is often to relax a fixed parameter, add a constraint that was forgotten, remove a constraint that was redundant, or fix an extraneous degree of freedom.\n",
+ "5. Model-specific intuition is usually necessary to diagnose and fix modeling issues. (If you're an algorithm developer, learn about the models you're using! If you don't understand your models, you don't understand your algorithms!)\n",
+ "6. A modeling issue doesn't necessarily have a unique solution. This is especially true when the issue involves invalid assumptions.\n",
+ "7. Debugging is an iterative process — fixing one issue can introduce another."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "33983d41",
+ "metadata": {},
+ "source": [
+ "# References\n",
+ "\n",
+ "[[1]] Okoli et al., \"A framework for the optimization of chemical looping combustion processes\". *Powder Tech*, 2020.\n",
+ "\n",
+ "[[2]] Parker and Biegler, \"Dynamic modeling and nonlinear model predictive control of a moving bed chemical looping combustion reactor\". *IFAC PapersOnline*, 2022.\n",
+ "\n",
+ "[[3]] Dulmage and Mendelsohn, \"Coverings of bipartite graphs\". *Can J. Math.*, 1958.\n",
+ "\n",
+ "[[4]] Pothen and Fan, \"Computing the block triangular form of a sparse matrix\". *ACM Trans. Math. Softw.*, 1990.\n",
+ "\n",
+ "[[5]] Parker et al., \"Applications of the Dulmage-Mendelsohn decomposition for debugging nonlinear optimization problems\". *Comp. Chem. Eng.*, 2023.\n",
+ "\n",
+ "[1]: https://www.sciencedirect.com/science/article/pii/S0032591019302803\n",
+ "[2]: https://www.sciencedirect.com/science/article/pii/S2405896322008825\n",
+ "[3]: https://www.cambridge.org/core/journals/canadian-journal-of-mathematics/article/coverings-of-bipartite-graphs/413735C5888AB542B92D0C4F402800B1\n",
+ "[4]: https://dl.acm.org/doi/10.1145/98267.98287\n",
+ "[5]: https://www.sciencedirect.com/science/article/pii/S0098135423002533\n"
+ ]
+ }
+ ],
+ "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.12.3"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/idaes_examples/notebooks/docs/diagnostics/structural_singularity_doc.ipynb b/idaes_examples/notebooks/docs/diagnostics/structural_singularity_doc.ipynb
new file mode 100644
index 00000000..29eb4cc5
--- /dev/null
+++ b/idaes_examples/notebooks/docs/diagnostics/structural_singularity_doc.ipynb
@@ -0,0 +1,701 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "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": [
+ "Debugging a Structural Singularity\n",
+ "===========================\n",
+ "Author: Robert Parker\\\n",
+ "Maintainer: Robert Parker\\\n",
+ "Updated: 2024-06-10\n",
+ "\n",
+ "In this tutorial, we will use the [IDAES Diagnostics Toolbox](https://idaes-pse.readthedocs.io/en/2.4.0/explanations/model_diagnostics/index.html#diagnostics-toolbox)\n",
+ "to diagnose and fix a structural singularity that is preventing us from solving an optimization problem."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "jp-MarkdownHeadingCollapsed": true
+ },
+ "source": [
+ "# Constructing the model\n",
+ "\n",
+ "Suppose a collaborator has given us a model to work with. They give us a square model and tell us what the degrees of freedom are. We construct an optimization problem and try to solve it. In this tutorial, we don't want to worry too much about the details that go into constructing the model. This has been provided in the `idaes_examples.mod.diagnostics.gas_solid_contactors.model` module.\n",
+ "\n",
+ "## Model details (OKAY TO SKIP)\n",
+ "\n",
+ "The model we are trying to optimize is a dynamic model of a moving bed chemical looping combustion reactor. The model has been described by [Okoli et al.][1] and [Parker and Biegler][2]. This is a gas-solid reactor with counter-current flow. The degrees of freedom are gas and solid inlet flow rates, and we are trying to minimize the deviation from a desired operating point via a least-squares objective function.\n",
+ "\n",
+ "[1]: https://www.sciencedirect.com/science/article/pii/S0032591019302803\n",
+ "[2]: https://www.sciencedirect.com/science/article/pii/S2405896322008825\n",
+ "\n",
+ "Again, we don't want to worry too much about the model. The `make_model` function will construct the optimization problem that we want to solve, and whenever we do something model-specific, we will explicitly make note of it."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Trying to solve the original model\n",
+ "\n",
+ "With that out of the way, let's construct the model and try to solve it!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from idaes_examples.mod.diagnostics.gas_solid_contactors.model import make_model\n",
+ "import logging\n",
+ "\n",
+ "# We'll turn off IDAES logging. This is not recommended in general, but this is an old model\n",
+ "# (from IDAES 1.7) that has been ported to work with the current version of IDAES. It generates\n",
+ "# a lot of warnings.\n",
+ "logging.getLogger(\"idaes\").setLevel(logging.CRITICAL)\n",
+ "# We'll also turn off Pyomo logging. This will suppress unit inconsistency warnings later,\n",
+ "# which otherwise flood our console and slow down this notebook. We have unit inconsistencies\n",
+ "# as, in IDAES 1.7, we didn't rigorously enforce that models use units.\n",
+ "logging.getLogger(\"pyomo\").setLevel(logging.CRITICAL)\n",
+ "\n",
+ "# This constructs a dynamic model with degrees of freedom and an objective function.\n",
+ "model = make_model()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Before trying to solve the model, let's make sure it conforms to our expectations, i.e. it (a) has degrees of freedom and (b) is well-initialized to a feasible point."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Import some useful utilities from the model_statistics module.\n",
+ "# Degrees of freedom and constraint residuals are always good things to check before\n",
+ "# trying to solve a simulation or optimization problem.\n",
+ "from idaes.core.util.model_statistics import degrees_of_freedom, large_residuals_set\n",
+ "\n",
+ "dof = degrees_of_freedom(model)\n",
+ "print(f\"Degrees of freedom: {dof}\")\n",
+ "has_large_residuals = bool(large_residuals_set(model, tol=1e-5))\n",
+ "print(f\"Has large residuals: {has_large_residuals}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In the above `make_model` function, the model has been \"solved\" to arrive at a feasible point, then degrees of freedom have been unfixed and an objective function has been added to give us an optimization problem. This looks good so far, so let's try to solve the optimization problem."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Import pyomo.environ for access to solvers\n",
+ "import pyomo.environ as pyo\n",
+ "\n",
+ "solver = pyo.SolverFactory(\"ipopt\")\n",
+ "solver.options[\"max_iter\"] = 20\n",
+ "solver.options[\"print_user_options\"] = \"yes\"\n",
+ "solver.options[\"OF_print_info_string\"] = \"yes\"\n",
+ "res = solver.solve(model, tee=True)\n",
+ "print(f\"Converged successfully: {pyo.check_optimal_termination(res)}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "IPOPT fails to solve the optimization problem... You can try increasing the iteration limit, but it is very unlikely that this model will ever solve. A telltale sign that something is wrong with our model is the persistence of regularization coefficients, that is, numbers in the `lg(rg)` column of the IPOPT log. These coefficients can have multiple causes. One is that the constraint Jacobian (partial derivative matrix) is singular, which indicates a problem with our model. We have set the `print_info_string` option in IPOPT to display \"diagnostic tags\" to help interpret these regularization coefficients. The \"L\" and \"l\" diagnostic tags, which appear repeatedly, indicate that the Jacobian is singular. For more information on IPOPT diagnostic tags, see the IPOPT [documentation](https://coin-or.github.io/Ipopt/OUTPUT.html)."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Debugging the original model\n",
+ "\n",
+ "Let's run the diagnostics toolbox on the model and see what it has to say.\n",
+ "\n",
+ "For good practice, we'll first make sure the model we're debugging is square. Remember that we're assuming we already know how to toggle degrees of freedom in our model."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Fix gas and solid flow rates at their respective inlets\n",
+ "model.fs.MB.gas_phase.properties[:, 0].flow_mol.fix()\n",
+ "model.fs.MB.solid_phase.properties[:, 1].flow_mass.fix()\n",
+ "# Part of our optimization problem was a set of constraints to enforce piecewise\n",
+ "# constant control inputs. We need to deactivate these as well.\n",
+ "model.piecewise_constant_constraints.deactivate()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now we can run the diagnostics toolbox."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from idaes.core.util.model_diagnostics import DiagnosticsToolbox\n",
+ "\n",
+ "dt = DiagnosticsToolbox(model)\n",
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let's look at the warnings we got:\n",
+ "- Inconsistent units\n",
+ "- Structural singularity\n",
+ "- Potential evaluation errors\n",
+ "\n",
+ "We'll ignore the inconsistent units. The property package and unit model here were extracted from IDAES 1.7, before we rigorously enforced that all models use units. The potential evaluation errors we see here may be worth looking into, but looking at the failing IPOPT log above, we don't notice any evaluation errors. (If evaluation errors occurred in IPOPT, we would see a message like \"Error in AMPL evaluation\" in the IPOPT iteration log, which we don't see here.) The structural singularity looks like the most promising avenue to debug, especially as the IPOPT log displays persistent regularization coefficients that appear to be caused by a singular Jacobian.\n",
+ "\n",
+ "Let's follow the toolbox's advice and display the under and over-constrained sets."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "dt.display_underconstrained_set()\n",
+ "dt.display_overconstrained_set()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Over and under-constrained subsystems\n",
+ "\n",
+ "Structural singularities are characterized by the [Dulmage-Mendelson decomposition][3], which partitions a system into minimal over and under-constrained subsystems. These subsystems contain the potentially unmatched constraints and variables, respectively. Here, \"unmatched\" effectively means \"causing a singularity\". [Pothen and Fan][4] give a good overview of the Dulmage-Mendelsohn decomposition and [Parker et al.][5] give several examples.\n",
+ "\n",
+ "[3]: https://www.cambridge.org/core/journals/canadian-journal-of-mathematics/article/coverings-of-bipartite-graphs/413735C5888AB542B92D0C4F402800B1\n",
+ "[4]: https://dl.acm.org/doi/10.1145/98267.98287\n",
+ "[5]: https://www.sciencedirect.com/science/article/pii/S0098135423002533\n",
+ "\n",
+ "The most straightforward way to fix a structural singularity is to fix variables that are in the under-constrained system and deactivate constraints in the over-constrained subsystem. However, this may not be applicable for every model. For example, we may need to add variables and constraints instead. What over and under-constrained subsystems are telling us is that something is wrong with our modeling assumptions. The particular fix that is appropriate will depend heavily on the model.\n",
+ "\n",
+ "If the above output gives us any clues, we can go ahead and start trying to fix things. However, suppose it doesn't. A good strategy is to try to break down the model into smaller, square subsystems that we think should be nonsingular. For a dynamic model like this one, a good candidate is the subsystem of variables and equations at each point in time."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# We've included a utility function to extract the subsystem of variables and equations\n",
+ "# at a specified point in time. If you are dealing with a process flowsheet, here you\n",
+ "# may want to extract each unit model individually.\n",
+ "from idaes_examples.mod.diagnostics.util import get_subsystem_at_time\n",
+ "\n",
+ "# TemporarySubsystemManager is used to temporarily fix some variables to make sure\n",
+ "# we're debugging a square subsystem.\n",
+ "from pyomo.util.subsystems import TemporarySubsystemManager\n",
+ "\n",
+ "# Let's start with t=0. Really, we'd probably want to do this in a loop and try all time points.\n",
+ "t0 = model.fs.time.first()\n",
+ "t_block, inputs = get_subsystem_at_time(model, model.fs.time, t0)\n",
+ "# We'll temporarily fix the \"inputs\" to make sure we have a square system while debugging\n",
+ "with TemporarySubsystemManager(to_fix=inputs):\n",
+ " dt = DiagnosticsToolbox(t_block)\n",
+ " dt.report_structural_issues()\n",
+ " dt.display_underconstrained_set()\n",
+ " dt.display_overconstrained_set()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "These over and under-constrained subsystems aren't much smaller, but now the over-constrained system decomposes into 10 small, independent blocks. These should be easier to debug.\n",
+ "\n",
+ "## Debugging the over-constrained subsystem\n",
+ "\n",
+ "To debug the over-constrained subsystem, we look for a constraint that is not calculating any of the variables in the subsystem. The \"odd constraint out\" here seems to be the mass fraction sum, `sum_component_eqn`. This must \"solve for\" one of the mass fractions, which means one of the `material_holdup_calculation` equations must \"solve for\" particle density rather than mass fraction. If we want to see what variables are contained in one of these constraints, we can always `pprint` it:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model.fs.MB.solid_phase.properties[0, 0.9].sum_component_eqn.pprint()\n",
+ "model.fs.MB.solid_phase.material_holdup_calculation[0, 0.9, \"Sol\", \"Fe3O4\"].pprint()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If one of these `material_holdup_calculation` equations is solving for particle density, then that means that `density_particle_constraint` is not actually solving for density. Maybe `density_particle_constraint` is over-determining our system?"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model.fs.MB.solid_phase.properties[0, 0.9].density_particle_constraint.pprint()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "But this looks like a very reasonable constraint. After some thought, which admittedly requires some knowledge of the process we are modeling, we decide that the right approach is to make particle porosity a variable. We have assumed that porosity is constant, but this overconstrained subsystem is telling us that this assumption is not valid.\n",
+ "\n",
+ "### How did we figure this out? (OKAY TO SKIP)\n",
+ "Adding a variable (including by unfixing a parameter) to an over-constraining constraint will often remove that constraint from the over-constrained subsystem. But how did we know that this was the right thing to do? If you just care about using the diagnostics toolbox to extract as much information about a singularity as possible, you can skip this section. But if you are curious how we determined that particle porosity should not be constant, read on.\n",
+ "\n",
+ "`dens_mass_skeletal` is determined purely by the composition of solid, which is made up of Fe2O3, Fe3O4, and inert Ti2O3. We can view the `density_skeletal_constraint` as follows:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model.fs.MB.solid_phase.properties[0, 0.9].density_skeletal_constraint.pprint()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If we assume a constant particle porosity, this gives us a particle porosity that is also uniquely determined by the solid composition by the above `density_particle_constraint`:\n",
+ "```\n",
+ "dens_mass_particle = (1 - porosity) * dens_mass_skeletal\n",
+ "```\n",
+ "But the composition of the solid is determined by the (somewhat misnamed) `material_holdup_calculation` constraints. While the name of these constraints implies they \"calculate holdups,\" material holdups at $t=0$ are fixed as initial conditions (because holdups are the differential variables with respect to time in this model). At other time points, we assume that holdups are specified by differential and discretization equations of the model. This means that the `material_holdup_calculation` constraints actually calculate the solid phase mass fractions *from* the holdups. But as we hinted at above, the 4-by-4 system of holdup calculation constraints, `sum_component_eqn` (which simply constrains the sum of mass fractions to be one), mass fractions, and `dens_mass_particle`, uniquely solve for `dens_mass_particle` *as well as* the mass fractions. But if the holdup variables can be used to solve for the mass fractions, they *also* solve for `dens_mass_skeletal`. So both sides of `density_particle_constraint` are already uniquely determined! This implies that we don't need this constraint at all, but we also know that this constraint has to hold. Something has to give. With this in mind, we actually have several options for how to resolve this overspecification:\n",
+ "1. Remove `density_particle_constraint`. Then we would have `dens_mass_particle` and `dens_mass_skeletal`, with no relationship between them. This would leave us with a mathematically sound model, but with densities that contradict constant particle porosity that we have assumed (which is used elsewhere in the reaction rate calculation equations).\n",
+ "2. Remove the constraints that calculate skeletal density from composition.\n",
+ "3. Relax particle porosity from a parameter to a variable.\n",
+ "\n",
+ "Options 2 and 3 are equally valid. We've chosen option 3, meaning we assume that the particle \"evolves\" with a density that is well determined from its constituent species, rather than changing density to accommodate whatever mass it accumulates via reaction without altering its volume. This exercise should remind us that all mathematical modeling is somewhat of an art. In the process of choosing the \"least bad\" model, it is fairly easy to over or under-specify something by making the wrong combination of assumptions, and the Dulmage-Mendelsohn decomposition is a great tool for detecting when this has happened.\n",
+ "\n",
+ "## Debugging the under-constrained subsystem\n",
+ "\n",
+ "The under-constrained system does not decompose into independent subsystems, making it more difficult to debug. However, by inspection, we notice that the same constraints and variables seem to be repeated at each point in the length domain. For each point in space, the \"odd variable out\" seems to be the total flow rate `flow_mass`. Using some intuition about this particular process model, we may conclude that this variable should be calculated from the solid phase velocity, which is constant. We expect an equation that looks like\n",
+ "```\n",
+ "flow_mass == velocity * area * density\n",
+ "```\n",
+ "\n",
+ "But this equation isn't here... so we need to add it.\n",
+ "\n",
+ "# Fixing the model\n",
+ "\n",
+ "We'll start by creating a fresh copy of the model, so we don't accidentally rely on IPOPT's point of termination."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2 = make_model()\n",
+ "# Make the model square while we try to fix the structural singularity\n",
+ "model2.fs.MB.gas_phase.properties[:, 0].flow_mol.fix()\n",
+ "model2.fs.MB.solid_phase.properties[:, 1].flow_mass.fix()\n",
+ "model2.piecewise_constant_constraints.deactivate()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Adding a new particle porosity variable"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2.fs.MB.particle_porosity = pyo.Var(\n",
+ " model2.fs.time,\n",
+ " model2.fs.MB.length_domain,\n",
+ " initialize=model2.fs.solid_properties.particle_porosity.value,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now we need to replace the old particle porosity parameter with this new variable. Luckily, the old parameter is actually implemented as a fixed variable, so we can easily identify all the constraints it participates in with `IncidenceGraphInterface`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from pyomo.contrib.incidence_analysis import IncidenceGraphInterface\n",
+ "\n",
+ "igraph = IncidenceGraphInterface(model2, include_fixed=True)\n",
+ "porosity_param = model2.fs.solid_properties.particle_porosity\n",
+ "print(f\"Constraints containing {porosity_param.name}:\")\n",
+ "for con in igraph.get_adjacent_to(porosity_param):\n",
+ " print(f\" {con.name}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Particle porosity only appears in two constraints: the density constraint we saw above, and the reaction rate equation. We can replace particle porosity in these constraints using Pyomo's `replace_expressions` function:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from pyomo.core.expr import replace_expressions\n",
+ "\n",
+ "for t, x in model2.fs.time * model2.fs.MB.length_domain:\n",
+ " substitution_map = {id(porosity_param): model2.fs.MB.particle_porosity[t, x]}\n",
+ " sp = model2.fs.MB.solid_phase\n",
+ " cons = [\n",
+ " sp.properties[t, x].density_particle_constraint,\n",
+ " sp.reactions[t, x].gen_rate_expression[\"R1\"],\n",
+ " ]\n",
+ " for con in cons:\n",
+ " con.set_value(\n",
+ " replace_expressions(\n",
+ " con.expr, substitution_map, descend_into_named_expressions=True\n",
+ " )\n",
+ " )"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We have added a new `particle_porosity` variable, and are using it in the relevant locations. Now we can move on to adding the missing constraint.\n",
+ "\n",
+ "## Adding a new density-flow rate constraint"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "@model2.fs.MB.Constraint(model2.fs.time, model2.fs.MB.length_domain)\n",
+ "def density_flowrate_constraint(mb, t, x):\n",
+ " return (\n",
+ " mb.velocity_superficial_solid[t]\n",
+ " * mb.bed_area\n",
+ " * mb.solid_phase.properties[t, x].dens_mass_particle\n",
+ " == mb.solid_phase.properties[t, x].flow_mass\n",
+ " )"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Testing the new model\n",
+ "\n",
+ "Let's see if these changes have fixed our model."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Construct a new diagnostics toolbox\n",
+ "dt = DiagnosticsToolbox(model2)\n",
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The structural singularity seems to be gone! Let's unfix our degrees of freedom and see if we can solve."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2.fs.MB.gas_phase.properties[:, 0].flow_mol.unfix()\n",
+ "model2.fs.MB.gas_phase.properties[0, 0].flow_mol.fix()\n",
+ "model2.fs.MB.solid_phase.properties[:, 1].flow_mass.unfix()\n",
+ "model2.fs.MB.solid_phase.properties[0, 1].flow_mass.fix()\n",
+ "model2.piecewise_constant_constraints.activate()\n",
+ "\n",
+ "res = solver.solve(model2, tee=True)\n",
+ "print(f\"Converged successfully: {pyo.check_optimal_termination(res)}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This doesn't look much better. What's going on? I thought we just fixed the issue?\n",
+ "\n",
+ "# Debugging the model, take two\n",
+ "\n",
+ "Let's check the diagnostics toolbox for numerical issues."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2.fs.MB.gas_phase.properties[:, 0].flow_mol.fix()\n",
+ "model2.fs.MB.solid_phase.properties[:, 1].flow_mass.fix()\n",
+ "model2.piecewise_constant_constraints.deactivate()\n",
+ "dt.report_numerical_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Looks like we have \"parallel constraints\", which are another form of singularity. Let's follow the toolbox's advice to see what they are."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "dt.display_near_parallel_constraints()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "`density_flowrate_constraint` is the constraint that we added. What is `solid_super_vel`?"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2.fs.MB.solid_super_vel[0].pprint()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This is the same as the constraint we just added! Looks like that constraint already existed at the solid inlet. We can easily deactivate the new constraints at this point in the length domain:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2.fs.MB.density_flowrate_constraint[:, 1.0].deactivate();"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "But now we have removed constraints from a square model, and expect to have degrees of freedom. Let's see what the diagnostics toolbox has to say."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "dt = DiagnosticsToolbox(model2)\n",
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "But this doesn't help us very much. We have some extraneous degrees of freedom, but with 8881 variables in the under-constrained subsystem, it will be difficult to tell what they are. After some thought (and model-specific intuition), we land on the conclusion that maybe we need to fix particle porosity at the solid inlet. Here, total flow rate is specified, and the `solid_super_vel` equation is using it to compute velocity. So we need `dens_mass_particle` to be known, which means we need `particle_porosity` to be fixed."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2.fs.MB.particle_porosity[:, 1.0].fix();"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let's run the diagnostics toolbox as a sanity check."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "dt = DiagnosticsToolbox(model2)\n",
+ "dt.report_structural_issues()\n",
+ "dt.report_numerical_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Looks good! Now we can release our degrees of freedom and try to solve again."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2.fs.MB.gas_phase.properties[:, 0].flow_mol.unfix()\n",
+ "model2.fs.MB.gas_phase.properties[0, 0].flow_mol.fix()\n",
+ "model2.fs.MB.solid_phase.properties[:, 1].flow_mass.unfix()\n",
+ "model2.fs.MB.solid_phase.properties[0, 1].flow_mass.fix()\n",
+ "model2.piecewise_constant_constraints.activate()\n",
+ "\n",
+ "res = solver.solve(model2, tee=True)\n",
+ "print(f\"Converged successfully: {pyo.check_optimal_termination(res)}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "It worked! For the simple optimization problem we have set up, this solve looks a lot more like what we expect.\n",
+ "\n",
+ "# Takeaways from this tutorial\n",
+ "What have we learned?\n",
+ "1. IPOPT using non-zero regularization coefficients hints at a singular Jacobian (especially when \"L\"/\"l\" diagnostic tags are present).\n",
+ "2. When this happens, start by calling `report_structural_issues` to check for a structural singularity. If this looks good, call `report_numerical_issues` to check for a numerical singularity.\n",
+ "3. When debugging a structural singularity, decomposing a problem into subsystems that each should be nonsingular (e.g. unit models or points in time) is very useful.\n",
+ "4. The solution to a structural singularity is often to relax a fixed parameter, add a constraint that was forgotten, remove a constraint that was redundant, or fix an extraneous degree of freedom.\n",
+ "5. Model-specific intuition is usually necessary to diagnose and fix modeling issues. (If you're an algorithm developer, learn about the models you're using! If you don't understand your models, you don't understand your algorithms!)\n",
+ "6. A modeling issue doesn't necessarily have a unique solution. This is especially true when the issue involves invalid assumptions.\n",
+ "7. Debugging is an iterative process — fixing one issue can introduce another."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# References\n",
+ "\n",
+ "[[1]] Okoli et al., \"A framework for the optimization of chemical looping combustion processes\". *Powder Tech*, 2020.\n",
+ "\n",
+ "[[2]] Parker and Biegler, \"Dynamic modeling and nonlinear model predictive control of a moving bed chemical looping combustion reactor\". *IFAC PapersOnline*, 2022.\n",
+ "\n",
+ "[[3]] Dulmage and Mendelsohn, \"Coverings of bipartite graphs\". *Can J. Math.*, 1958.\n",
+ "\n",
+ "[[4]] Pothen and Fan, \"Computing the block triangular form of a sparse matrix\". *ACM Trans. Math. Softw.*, 1990.\n",
+ "\n",
+ "[[5]] Parker et al., \"Applications of the Dulmage-Mendelsohn decomposition for debugging nonlinear optimization problems\". *Comp. Chem. Eng.*, 2023.\n",
+ "\n",
+ "[1]: https://www.sciencedirect.com/science/article/pii/S0032591019302803\n",
+ "[2]: https://www.sciencedirect.com/science/article/pii/S2405896322008825\n",
+ "[3]: https://www.cambridge.org/core/journals/canadian-journal-of-mathematics/article/coverings-of-bipartite-graphs/413735C5888AB542B92D0C4F402800B1\n",
+ "[4]: https://dl.acm.org/doi/10.1145/98267.98287\n",
+ "[5]: https://www.sciencedirect.com/science/article/pii/S0098135423002533\n"
+ ]
+ }
+ ],
+ "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.12.3"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 3
+}
\ No newline at end of file
diff --git a/idaes_examples/notebooks/docs/diagnostics/structural_singularity_test.ipynb b/idaes_examples/notebooks/docs/diagnostics/structural_singularity_test.ipynb
new file mode 100644
index 00000000..29eb4cc5
--- /dev/null
+++ b/idaes_examples/notebooks/docs/diagnostics/structural_singularity_test.ipynb
@@ -0,0 +1,701 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "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": [
+ "Debugging a Structural Singularity\n",
+ "===========================\n",
+ "Author: Robert Parker\\\n",
+ "Maintainer: Robert Parker\\\n",
+ "Updated: 2024-06-10\n",
+ "\n",
+ "In this tutorial, we will use the [IDAES Diagnostics Toolbox](https://idaes-pse.readthedocs.io/en/2.4.0/explanations/model_diagnostics/index.html#diagnostics-toolbox)\n",
+ "to diagnose and fix a structural singularity that is preventing us from solving an optimization problem."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "jp-MarkdownHeadingCollapsed": true
+ },
+ "source": [
+ "# Constructing the model\n",
+ "\n",
+ "Suppose a collaborator has given us a model to work with. They give us a square model and tell us what the degrees of freedom are. We construct an optimization problem and try to solve it. In this tutorial, we don't want to worry too much about the details that go into constructing the model. This has been provided in the `idaes_examples.mod.diagnostics.gas_solid_contactors.model` module.\n",
+ "\n",
+ "## Model details (OKAY TO SKIP)\n",
+ "\n",
+ "The model we are trying to optimize is a dynamic model of a moving bed chemical looping combustion reactor. The model has been described by [Okoli et al.][1] and [Parker and Biegler][2]. This is a gas-solid reactor with counter-current flow. The degrees of freedom are gas and solid inlet flow rates, and we are trying to minimize the deviation from a desired operating point via a least-squares objective function.\n",
+ "\n",
+ "[1]: https://www.sciencedirect.com/science/article/pii/S0032591019302803\n",
+ "[2]: https://www.sciencedirect.com/science/article/pii/S2405896322008825\n",
+ "\n",
+ "Again, we don't want to worry too much about the model. The `make_model` function will construct the optimization problem that we want to solve, and whenever we do something model-specific, we will explicitly make note of it."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Trying to solve the original model\n",
+ "\n",
+ "With that out of the way, let's construct the model and try to solve it!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from idaes_examples.mod.diagnostics.gas_solid_contactors.model import make_model\n",
+ "import logging\n",
+ "\n",
+ "# We'll turn off IDAES logging. This is not recommended in general, but this is an old model\n",
+ "# (from IDAES 1.7) that has been ported to work with the current version of IDAES. It generates\n",
+ "# a lot of warnings.\n",
+ "logging.getLogger(\"idaes\").setLevel(logging.CRITICAL)\n",
+ "# We'll also turn off Pyomo logging. This will suppress unit inconsistency warnings later,\n",
+ "# which otherwise flood our console and slow down this notebook. We have unit inconsistencies\n",
+ "# as, in IDAES 1.7, we didn't rigorously enforce that models use units.\n",
+ "logging.getLogger(\"pyomo\").setLevel(logging.CRITICAL)\n",
+ "\n",
+ "# This constructs a dynamic model with degrees of freedom and an objective function.\n",
+ "model = make_model()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Before trying to solve the model, let's make sure it conforms to our expectations, i.e. it (a) has degrees of freedom and (b) is well-initialized to a feasible point."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Import some useful utilities from the model_statistics module.\n",
+ "# Degrees of freedom and constraint residuals are always good things to check before\n",
+ "# trying to solve a simulation or optimization problem.\n",
+ "from idaes.core.util.model_statistics import degrees_of_freedom, large_residuals_set\n",
+ "\n",
+ "dof = degrees_of_freedom(model)\n",
+ "print(f\"Degrees of freedom: {dof}\")\n",
+ "has_large_residuals = bool(large_residuals_set(model, tol=1e-5))\n",
+ "print(f\"Has large residuals: {has_large_residuals}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In the above `make_model` function, the model has been \"solved\" to arrive at a feasible point, then degrees of freedom have been unfixed and an objective function has been added to give us an optimization problem. This looks good so far, so let's try to solve the optimization problem."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Import pyomo.environ for access to solvers\n",
+ "import pyomo.environ as pyo\n",
+ "\n",
+ "solver = pyo.SolverFactory(\"ipopt\")\n",
+ "solver.options[\"max_iter\"] = 20\n",
+ "solver.options[\"print_user_options\"] = \"yes\"\n",
+ "solver.options[\"OF_print_info_string\"] = \"yes\"\n",
+ "res = solver.solve(model, tee=True)\n",
+ "print(f\"Converged successfully: {pyo.check_optimal_termination(res)}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "IPOPT fails to solve the optimization problem... You can try increasing the iteration limit, but it is very unlikely that this model will ever solve. A telltale sign that something is wrong with our model is the persistence of regularization coefficients, that is, numbers in the `lg(rg)` column of the IPOPT log. These coefficients can have multiple causes. One is that the constraint Jacobian (partial derivative matrix) is singular, which indicates a problem with our model. We have set the `print_info_string` option in IPOPT to display \"diagnostic tags\" to help interpret these regularization coefficients. The \"L\" and \"l\" diagnostic tags, which appear repeatedly, indicate that the Jacobian is singular. For more information on IPOPT diagnostic tags, see the IPOPT [documentation](https://coin-or.github.io/Ipopt/OUTPUT.html)."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Debugging the original model\n",
+ "\n",
+ "Let's run the diagnostics toolbox on the model and see what it has to say.\n",
+ "\n",
+ "For good practice, we'll first make sure the model we're debugging is square. Remember that we're assuming we already know how to toggle degrees of freedom in our model."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Fix gas and solid flow rates at their respective inlets\n",
+ "model.fs.MB.gas_phase.properties[:, 0].flow_mol.fix()\n",
+ "model.fs.MB.solid_phase.properties[:, 1].flow_mass.fix()\n",
+ "# Part of our optimization problem was a set of constraints to enforce piecewise\n",
+ "# constant control inputs. We need to deactivate these as well.\n",
+ "model.piecewise_constant_constraints.deactivate()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now we can run the diagnostics toolbox."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from idaes.core.util.model_diagnostics import DiagnosticsToolbox\n",
+ "\n",
+ "dt = DiagnosticsToolbox(model)\n",
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let's look at the warnings we got:\n",
+ "- Inconsistent units\n",
+ "- Structural singularity\n",
+ "- Potential evaluation errors\n",
+ "\n",
+ "We'll ignore the inconsistent units. The property package and unit model here were extracted from IDAES 1.7, before we rigorously enforced that all models use units. The potential evaluation errors we see here may be worth looking into, but looking at the failing IPOPT log above, we don't notice any evaluation errors. (If evaluation errors occurred in IPOPT, we would see a message like \"Error in AMPL evaluation\" in the IPOPT iteration log, which we don't see here.) The structural singularity looks like the most promising avenue to debug, especially as the IPOPT log displays persistent regularization coefficients that appear to be caused by a singular Jacobian.\n",
+ "\n",
+ "Let's follow the toolbox's advice and display the under and over-constrained sets."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "dt.display_underconstrained_set()\n",
+ "dt.display_overconstrained_set()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Over and under-constrained subsystems\n",
+ "\n",
+ "Structural singularities are characterized by the [Dulmage-Mendelson decomposition][3], which partitions a system into minimal over and under-constrained subsystems. These subsystems contain the potentially unmatched constraints and variables, respectively. Here, \"unmatched\" effectively means \"causing a singularity\". [Pothen and Fan][4] give a good overview of the Dulmage-Mendelsohn decomposition and [Parker et al.][5] give several examples.\n",
+ "\n",
+ "[3]: https://www.cambridge.org/core/journals/canadian-journal-of-mathematics/article/coverings-of-bipartite-graphs/413735C5888AB542B92D0C4F402800B1\n",
+ "[4]: https://dl.acm.org/doi/10.1145/98267.98287\n",
+ "[5]: https://www.sciencedirect.com/science/article/pii/S0098135423002533\n",
+ "\n",
+ "The most straightforward way to fix a structural singularity is to fix variables that are in the under-constrained system and deactivate constraints in the over-constrained subsystem. However, this may not be applicable for every model. For example, we may need to add variables and constraints instead. What over and under-constrained subsystems are telling us is that something is wrong with our modeling assumptions. The particular fix that is appropriate will depend heavily on the model.\n",
+ "\n",
+ "If the above output gives us any clues, we can go ahead and start trying to fix things. However, suppose it doesn't. A good strategy is to try to break down the model into smaller, square subsystems that we think should be nonsingular. For a dynamic model like this one, a good candidate is the subsystem of variables and equations at each point in time."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# We've included a utility function to extract the subsystem of variables and equations\n",
+ "# at a specified point in time. If you are dealing with a process flowsheet, here you\n",
+ "# may want to extract each unit model individually.\n",
+ "from idaes_examples.mod.diagnostics.util import get_subsystem_at_time\n",
+ "\n",
+ "# TemporarySubsystemManager is used to temporarily fix some variables to make sure\n",
+ "# we're debugging a square subsystem.\n",
+ "from pyomo.util.subsystems import TemporarySubsystemManager\n",
+ "\n",
+ "# Let's start with t=0. Really, we'd probably want to do this in a loop and try all time points.\n",
+ "t0 = model.fs.time.first()\n",
+ "t_block, inputs = get_subsystem_at_time(model, model.fs.time, t0)\n",
+ "# We'll temporarily fix the \"inputs\" to make sure we have a square system while debugging\n",
+ "with TemporarySubsystemManager(to_fix=inputs):\n",
+ " dt = DiagnosticsToolbox(t_block)\n",
+ " dt.report_structural_issues()\n",
+ " dt.display_underconstrained_set()\n",
+ " dt.display_overconstrained_set()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "These over and under-constrained subsystems aren't much smaller, but now the over-constrained system decomposes into 10 small, independent blocks. These should be easier to debug.\n",
+ "\n",
+ "## Debugging the over-constrained subsystem\n",
+ "\n",
+ "To debug the over-constrained subsystem, we look for a constraint that is not calculating any of the variables in the subsystem. The \"odd constraint out\" here seems to be the mass fraction sum, `sum_component_eqn`. This must \"solve for\" one of the mass fractions, which means one of the `material_holdup_calculation` equations must \"solve for\" particle density rather than mass fraction. If we want to see what variables are contained in one of these constraints, we can always `pprint` it:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model.fs.MB.solid_phase.properties[0, 0.9].sum_component_eqn.pprint()\n",
+ "model.fs.MB.solid_phase.material_holdup_calculation[0, 0.9, \"Sol\", \"Fe3O4\"].pprint()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If one of these `material_holdup_calculation` equations is solving for particle density, then that means that `density_particle_constraint` is not actually solving for density. Maybe `density_particle_constraint` is over-determining our system?"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model.fs.MB.solid_phase.properties[0, 0.9].density_particle_constraint.pprint()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "But this looks like a very reasonable constraint. After some thought, which admittedly requires some knowledge of the process we are modeling, we decide that the right approach is to make particle porosity a variable. We have assumed that porosity is constant, but this overconstrained subsystem is telling us that this assumption is not valid.\n",
+ "\n",
+ "### How did we figure this out? (OKAY TO SKIP)\n",
+ "Adding a variable (including by unfixing a parameter) to an over-constraining constraint will often remove that constraint from the over-constrained subsystem. But how did we know that this was the right thing to do? If you just care about using the diagnostics toolbox to extract as much information about a singularity as possible, you can skip this section. But if you are curious how we determined that particle porosity should not be constant, read on.\n",
+ "\n",
+ "`dens_mass_skeletal` is determined purely by the composition of solid, which is made up of Fe2O3, Fe3O4, and inert Ti2O3. We can view the `density_skeletal_constraint` as follows:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model.fs.MB.solid_phase.properties[0, 0.9].density_skeletal_constraint.pprint()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If we assume a constant particle porosity, this gives us a particle porosity that is also uniquely determined by the solid composition by the above `density_particle_constraint`:\n",
+ "```\n",
+ "dens_mass_particle = (1 - porosity) * dens_mass_skeletal\n",
+ "```\n",
+ "But the composition of the solid is determined by the (somewhat misnamed) `material_holdup_calculation` constraints. While the name of these constraints implies they \"calculate holdups,\" material holdups at $t=0$ are fixed as initial conditions (because holdups are the differential variables with respect to time in this model). At other time points, we assume that holdups are specified by differential and discretization equations of the model. This means that the `material_holdup_calculation` constraints actually calculate the solid phase mass fractions *from* the holdups. But as we hinted at above, the 4-by-4 system of holdup calculation constraints, `sum_component_eqn` (which simply constrains the sum of mass fractions to be one), mass fractions, and `dens_mass_particle`, uniquely solve for `dens_mass_particle` *as well as* the mass fractions. But if the holdup variables can be used to solve for the mass fractions, they *also* solve for `dens_mass_skeletal`. So both sides of `density_particle_constraint` are already uniquely determined! This implies that we don't need this constraint at all, but we also know that this constraint has to hold. Something has to give. With this in mind, we actually have several options for how to resolve this overspecification:\n",
+ "1. Remove `density_particle_constraint`. Then we would have `dens_mass_particle` and `dens_mass_skeletal`, with no relationship between them. This would leave us with a mathematically sound model, but with densities that contradict constant particle porosity that we have assumed (which is used elsewhere in the reaction rate calculation equations).\n",
+ "2. Remove the constraints that calculate skeletal density from composition.\n",
+ "3. Relax particle porosity from a parameter to a variable.\n",
+ "\n",
+ "Options 2 and 3 are equally valid. We've chosen option 3, meaning we assume that the particle \"evolves\" with a density that is well determined from its constituent species, rather than changing density to accommodate whatever mass it accumulates via reaction without altering its volume. This exercise should remind us that all mathematical modeling is somewhat of an art. In the process of choosing the \"least bad\" model, it is fairly easy to over or under-specify something by making the wrong combination of assumptions, and the Dulmage-Mendelsohn decomposition is a great tool for detecting when this has happened.\n",
+ "\n",
+ "## Debugging the under-constrained subsystem\n",
+ "\n",
+ "The under-constrained system does not decompose into independent subsystems, making it more difficult to debug. However, by inspection, we notice that the same constraints and variables seem to be repeated at each point in the length domain. For each point in space, the \"odd variable out\" seems to be the total flow rate `flow_mass`. Using some intuition about this particular process model, we may conclude that this variable should be calculated from the solid phase velocity, which is constant. We expect an equation that looks like\n",
+ "```\n",
+ "flow_mass == velocity * area * density\n",
+ "```\n",
+ "\n",
+ "But this equation isn't here... so we need to add it.\n",
+ "\n",
+ "# Fixing the model\n",
+ "\n",
+ "We'll start by creating a fresh copy of the model, so we don't accidentally rely on IPOPT's point of termination."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2 = make_model()\n",
+ "# Make the model square while we try to fix the structural singularity\n",
+ "model2.fs.MB.gas_phase.properties[:, 0].flow_mol.fix()\n",
+ "model2.fs.MB.solid_phase.properties[:, 1].flow_mass.fix()\n",
+ "model2.piecewise_constant_constraints.deactivate()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Adding a new particle porosity variable"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2.fs.MB.particle_porosity = pyo.Var(\n",
+ " model2.fs.time,\n",
+ " model2.fs.MB.length_domain,\n",
+ " initialize=model2.fs.solid_properties.particle_porosity.value,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now we need to replace the old particle porosity parameter with this new variable. Luckily, the old parameter is actually implemented as a fixed variable, so we can easily identify all the constraints it participates in with `IncidenceGraphInterface`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from pyomo.contrib.incidence_analysis import IncidenceGraphInterface\n",
+ "\n",
+ "igraph = IncidenceGraphInterface(model2, include_fixed=True)\n",
+ "porosity_param = model2.fs.solid_properties.particle_porosity\n",
+ "print(f\"Constraints containing {porosity_param.name}:\")\n",
+ "for con in igraph.get_adjacent_to(porosity_param):\n",
+ " print(f\" {con.name}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Particle porosity only appears in two constraints: the density constraint we saw above, and the reaction rate equation. We can replace particle porosity in these constraints using Pyomo's `replace_expressions` function:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from pyomo.core.expr import replace_expressions\n",
+ "\n",
+ "for t, x in model2.fs.time * model2.fs.MB.length_domain:\n",
+ " substitution_map = {id(porosity_param): model2.fs.MB.particle_porosity[t, x]}\n",
+ " sp = model2.fs.MB.solid_phase\n",
+ " cons = [\n",
+ " sp.properties[t, x].density_particle_constraint,\n",
+ " sp.reactions[t, x].gen_rate_expression[\"R1\"],\n",
+ " ]\n",
+ " for con in cons:\n",
+ " con.set_value(\n",
+ " replace_expressions(\n",
+ " con.expr, substitution_map, descend_into_named_expressions=True\n",
+ " )\n",
+ " )"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We have added a new `particle_porosity` variable, and are using it in the relevant locations. Now we can move on to adding the missing constraint.\n",
+ "\n",
+ "## Adding a new density-flow rate constraint"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "@model2.fs.MB.Constraint(model2.fs.time, model2.fs.MB.length_domain)\n",
+ "def density_flowrate_constraint(mb, t, x):\n",
+ " return (\n",
+ " mb.velocity_superficial_solid[t]\n",
+ " * mb.bed_area\n",
+ " * mb.solid_phase.properties[t, x].dens_mass_particle\n",
+ " == mb.solid_phase.properties[t, x].flow_mass\n",
+ " )"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Testing the new model\n",
+ "\n",
+ "Let's see if these changes have fixed our model."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Construct a new diagnostics toolbox\n",
+ "dt = DiagnosticsToolbox(model2)\n",
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The structural singularity seems to be gone! Let's unfix our degrees of freedom and see if we can solve."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2.fs.MB.gas_phase.properties[:, 0].flow_mol.unfix()\n",
+ "model2.fs.MB.gas_phase.properties[0, 0].flow_mol.fix()\n",
+ "model2.fs.MB.solid_phase.properties[:, 1].flow_mass.unfix()\n",
+ "model2.fs.MB.solid_phase.properties[0, 1].flow_mass.fix()\n",
+ "model2.piecewise_constant_constraints.activate()\n",
+ "\n",
+ "res = solver.solve(model2, tee=True)\n",
+ "print(f\"Converged successfully: {pyo.check_optimal_termination(res)}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This doesn't look much better. What's going on? I thought we just fixed the issue?\n",
+ "\n",
+ "# Debugging the model, take two\n",
+ "\n",
+ "Let's check the diagnostics toolbox for numerical issues."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2.fs.MB.gas_phase.properties[:, 0].flow_mol.fix()\n",
+ "model2.fs.MB.solid_phase.properties[:, 1].flow_mass.fix()\n",
+ "model2.piecewise_constant_constraints.deactivate()\n",
+ "dt.report_numerical_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Looks like we have \"parallel constraints\", which are another form of singularity. Let's follow the toolbox's advice to see what they are."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "dt.display_near_parallel_constraints()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "`density_flowrate_constraint` is the constraint that we added. What is `solid_super_vel`?"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2.fs.MB.solid_super_vel[0].pprint()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This is the same as the constraint we just added! Looks like that constraint already existed at the solid inlet. We can easily deactivate the new constraints at this point in the length domain:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2.fs.MB.density_flowrate_constraint[:, 1.0].deactivate();"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "But now we have removed constraints from a square model, and expect to have degrees of freedom. Let's see what the diagnostics toolbox has to say."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "dt = DiagnosticsToolbox(model2)\n",
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "But this doesn't help us very much. We have some extraneous degrees of freedom, but with 8881 variables in the under-constrained subsystem, it will be difficult to tell what they are. After some thought (and model-specific intuition), we land on the conclusion that maybe we need to fix particle porosity at the solid inlet. Here, total flow rate is specified, and the `solid_super_vel` equation is using it to compute velocity. So we need `dens_mass_particle` to be known, which means we need `particle_porosity` to be fixed."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2.fs.MB.particle_porosity[:, 1.0].fix();"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let's run the diagnostics toolbox as a sanity check."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "dt = DiagnosticsToolbox(model2)\n",
+ "dt.report_structural_issues()\n",
+ "dt.report_numerical_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Looks good! Now we can release our degrees of freedom and try to solve again."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2.fs.MB.gas_phase.properties[:, 0].flow_mol.unfix()\n",
+ "model2.fs.MB.gas_phase.properties[0, 0].flow_mol.fix()\n",
+ "model2.fs.MB.solid_phase.properties[:, 1].flow_mass.unfix()\n",
+ "model2.fs.MB.solid_phase.properties[0, 1].flow_mass.fix()\n",
+ "model2.piecewise_constant_constraints.activate()\n",
+ "\n",
+ "res = solver.solve(model2, tee=True)\n",
+ "print(f\"Converged successfully: {pyo.check_optimal_termination(res)}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "It worked! For the simple optimization problem we have set up, this solve looks a lot more like what we expect.\n",
+ "\n",
+ "# Takeaways from this tutorial\n",
+ "What have we learned?\n",
+ "1. IPOPT using non-zero regularization coefficients hints at a singular Jacobian (especially when \"L\"/\"l\" diagnostic tags are present).\n",
+ "2. When this happens, start by calling `report_structural_issues` to check for a structural singularity. If this looks good, call `report_numerical_issues` to check for a numerical singularity.\n",
+ "3. When debugging a structural singularity, decomposing a problem into subsystems that each should be nonsingular (e.g. unit models or points in time) is very useful.\n",
+ "4. The solution to a structural singularity is often to relax a fixed parameter, add a constraint that was forgotten, remove a constraint that was redundant, or fix an extraneous degree of freedom.\n",
+ "5. Model-specific intuition is usually necessary to diagnose and fix modeling issues. (If you're an algorithm developer, learn about the models you're using! If you don't understand your models, you don't understand your algorithms!)\n",
+ "6. A modeling issue doesn't necessarily have a unique solution. This is especially true when the issue involves invalid assumptions.\n",
+ "7. Debugging is an iterative process — fixing one issue can introduce another."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# References\n",
+ "\n",
+ "[[1]] Okoli et al., \"A framework for the optimization of chemical looping combustion processes\". *Powder Tech*, 2020.\n",
+ "\n",
+ "[[2]] Parker and Biegler, \"Dynamic modeling and nonlinear model predictive control of a moving bed chemical looping combustion reactor\". *IFAC PapersOnline*, 2022.\n",
+ "\n",
+ "[[3]] Dulmage and Mendelsohn, \"Coverings of bipartite graphs\". *Can J. Math.*, 1958.\n",
+ "\n",
+ "[[4]] Pothen and Fan, \"Computing the block triangular form of a sparse matrix\". *ACM Trans. Math. Softw.*, 1990.\n",
+ "\n",
+ "[[5]] Parker et al., \"Applications of the Dulmage-Mendelsohn decomposition for debugging nonlinear optimization problems\". *Comp. Chem. Eng.*, 2023.\n",
+ "\n",
+ "[1]: https://www.sciencedirect.com/science/article/pii/S0032591019302803\n",
+ "[2]: https://www.sciencedirect.com/science/article/pii/S2405896322008825\n",
+ "[3]: https://www.cambridge.org/core/journals/canadian-journal-of-mathematics/article/coverings-of-bipartite-graphs/413735C5888AB542B92D0C4F402800B1\n",
+ "[4]: https://dl.acm.org/doi/10.1145/98267.98287\n",
+ "[5]: https://www.sciencedirect.com/science/article/pii/S0098135423002533\n"
+ ]
+ }
+ ],
+ "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.12.3"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 3
+}
\ No newline at end of file
diff --git a/idaes_examples/notebooks/docs/diagnostics/structural_singularity_usr.ipynb b/idaes_examples/notebooks/docs/diagnostics/structural_singularity_usr.ipynb
new file mode 100644
index 00000000..29eb4cc5
--- /dev/null
+++ b/idaes_examples/notebooks/docs/diagnostics/structural_singularity_usr.ipynb
@@ -0,0 +1,701 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "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": [
+ "Debugging a Structural Singularity\n",
+ "===========================\n",
+ "Author: Robert Parker\\\n",
+ "Maintainer: Robert Parker\\\n",
+ "Updated: 2024-06-10\n",
+ "\n",
+ "In this tutorial, we will use the [IDAES Diagnostics Toolbox](https://idaes-pse.readthedocs.io/en/2.4.0/explanations/model_diagnostics/index.html#diagnostics-toolbox)\n",
+ "to diagnose and fix a structural singularity that is preventing us from solving an optimization problem."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "jp-MarkdownHeadingCollapsed": true
+ },
+ "source": [
+ "# Constructing the model\n",
+ "\n",
+ "Suppose a collaborator has given us a model to work with. They give us a square model and tell us what the degrees of freedom are. We construct an optimization problem and try to solve it. In this tutorial, we don't want to worry too much about the details that go into constructing the model. This has been provided in the `idaes_examples.mod.diagnostics.gas_solid_contactors.model` module.\n",
+ "\n",
+ "## Model details (OKAY TO SKIP)\n",
+ "\n",
+ "The model we are trying to optimize is a dynamic model of a moving bed chemical looping combustion reactor. The model has been described by [Okoli et al.][1] and [Parker and Biegler][2]. This is a gas-solid reactor with counter-current flow. The degrees of freedom are gas and solid inlet flow rates, and we are trying to minimize the deviation from a desired operating point via a least-squares objective function.\n",
+ "\n",
+ "[1]: https://www.sciencedirect.com/science/article/pii/S0032591019302803\n",
+ "[2]: https://www.sciencedirect.com/science/article/pii/S2405896322008825\n",
+ "\n",
+ "Again, we don't want to worry too much about the model. The `make_model` function will construct the optimization problem that we want to solve, and whenever we do something model-specific, we will explicitly make note of it."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Trying to solve the original model\n",
+ "\n",
+ "With that out of the way, let's construct the model and try to solve it!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from idaes_examples.mod.diagnostics.gas_solid_contactors.model import make_model\n",
+ "import logging\n",
+ "\n",
+ "# We'll turn off IDAES logging. This is not recommended in general, but this is an old model\n",
+ "# (from IDAES 1.7) that has been ported to work with the current version of IDAES. It generates\n",
+ "# a lot of warnings.\n",
+ "logging.getLogger(\"idaes\").setLevel(logging.CRITICAL)\n",
+ "# We'll also turn off Pyomo logging. This will suppress unit inconsistency warnings later,\n",
+ "# which otherwise flood our console and slow down this notebook. We have unit inconsistencies\n",
+ "# as, in IDAES 1.7, we didn't rigorously enforce that models use units.\n",
+ "logging.getLogger(\"pyomo\").setLevel(logging.CRITICAL)\n",
+ "\n",
+ "# This constructs a dynamic model with degrees of freedom and an objective function.\n",
+ "model = make_model()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Before trying to solve the model, let's make sure it conforms to our expectations, i.e. it (a) has degrees of freedom and (b) is well-initialized to a feasible point."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Import some useful utilities from the model_statistics module.\n",
+ "# Degrees of freedom and constraint residuals are always good things to check before\n",
+ "# trying to solve a simulation or optimization problem.\n",
+ "from idaes.core.util.model_statistics import degrees_of_freedom, large_residuals_set\n",
+ "\n",
+ "dof = degrees_of_freedom(model)\n",
+ "print(f\"Degrees of freedom: {dof}\")\n",
+ "has_large_residuals = bool(large_residuals_set(model, tol=1e-5))\n",
+ "print(f\"Has large residuals: {has_large_residuals}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In the above `make_model` function, the model has been \"solved\" to arrive at a feasible point, then degrees of freedom have been unfixed and an objective function has been added to give us an optimization problem. This looks good so far, so let's try to solve the optimization problem."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Import pyomo.environ for access to solvers\n",
+ "import pyomo.environ as pyo\n",
+ "\n",
+ "solver = pyo.SolverFactory(\"ipopt\")\n",
+ "solver.options[\"max_iter\"] = 20\n",
+ "solver.options[\"print_user_options\"] = \"yes\"\n",
+ "solver.options[\"OF_print_info_string\"] = \"yes\"\n",
+ "res = solver.solve(model, tee=True)\n",
+ "print(f\"Converged successfully: {pyo.check_optimal_termination(res)}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "IPOPT fails to solve the optimization problem... You can try increasing the iteration limit, but it is very unlikely that this model will ever solve. A telltale sign that something is wrong with our model is the persistence of regularization coefficients, that is, numbers in the `lg(rg)` column of the IPOPT log. These coefficients can have multiple causes. One is that the constraint Jacobian (partial derivative matrix) is singular, which indicates a problem with our model. We have set the `print_info_string` option in IPOPT to display \"diagnostic tags\" to help interpret these regularization coefficients. The \"L\" and \"l\" diagnostic tags, which appear repeatedly, indicate that the Jacobian is singular. For more information on IPOPT diagnostic tags, see the IPOPT [documentation](https://coin-or.github.io/Ipopt/OUTPUT.html)."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Debugging the original model\n",
+ "\n",
+ "Let's run the diagnostics toolbox on the model and see what it has to say.\n",
+ "\n",
+ "For good practice, we'll first make sure the model we're debugging is square. Remember that we're assuming we already know how to toggle degrees of freedom in our model."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Fix gas and solid flow rates at their respective inlets\n",
+ "model.fs.MB.gas_phase.properties[:, 0].flow_mol.fix()\n",
+ "model.fs.MB.solid_phase.properties[:, 1].flow_mass.fix()\n",
+ "# Part of our optimization problem was a set of constraints to enforce piecewise\n",
+ "# constant control inputs. We need to deactivate these as well.\n",
+ "model.piecewise_constant_constraints.deactivate()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now we can run the diagnostics toolbox."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from idaes.core.util.model_diagnostics import DiagnosticsToolbox\n",
+ "\n",
+ "dt = DiagnosticsToolbox(model)\n",
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let's look at the warnings we got:\n",
+ "- Inconsistent units\n",
+ "- Structural singularity\n",
+ "- Potential evaluation errors\n",
+ "\n",
+ "We'll ignore the inconsistent units. The property package and unit model here were extracted from IDAES 1.7, before we rigorously enforced that all models use units. The potential evaluation errors we see here may be worth looking into, but looking at the failing IPOPT log above, we don't notice any evaluation errors. (If evaluation errors occurred in IPOPT, we would see a message like \"Error in AMPL evaluation\" in the IPOPT iteration log, which we don't see here.) The structural singularity looks like the most promising avenue to debug, especially as the IPOPT log displays persistent regularization coefficients that appear to be caused by a singular Jacobian.\n",
+ "\n",
+ "Let's follow the toolbox's advice and display the under and over-constrained sets."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "dt.display_underconstrained_set()\n",
+ "dt.display_overconstrained_set()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Over and under-constrained subsystems\n",
+ "\n",
+ "Structural singularities are characterized by the [Dulmage-Mendelson decomposition][3], which partitions a system into minimal over and under-constrained subsystems. These subsystems contain the potentially unmatched constraints and variables, respectively. Here, \"unmatched\" effectively means \"causing a singularity\". [Pothen and Fan][4] give a good overview of the Dulmage-Mendelsohn decomposition and [Parker et al.][5] give several examples.\n",
+ "\n",
+ "[3]: https://www.cambridge.org/core/journals/canadian-journal-of-mathematics/article/coverings-of-bipartite-graphs/413735C5888AB542B92D0C4F402800B1\n",
+ "[4]: https://dl.acm.org/doi/10.1145/98267.98287\n",
+ "[5]: https://www.sciencedirect.com/science/article/pii/S0098135423002533\n",
+ "\n",
+ "The most straightforward way to fix a structural singularity is to fix variables that are in the under-constrained system and deactivate constraints in the over-constrained subsystem. However, this may not be applicable for every model. For example, we may need to add variables and constraints instead. What over and under-constrained subsystems are telling us is that something is wrong with our modeling assumptions. The particular fix that is appropriate will depend heavily on the model.\n",
+ "\n",
+ "If the above output gives us any clues, we can go ahead and start trying to fix things. However, suppose it doesn't. A good strategy is to try to break down the model into smaller, square subsystems that we think should be nonsingular. For a dynamic model like this one, a good candidate is the subsystem of variables and equations at each point in time."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# We've included a utility function to extract the subsystem of variables and equations\n",
+ "# at a specified point in time. If you are dealing with a process flowsheet, here you\n",
+ "# may want to extract each unit model individually.\n",
+ "from idaes_examples.mod.diagnostics.util import get_subsystem_at_time\n",
+ "\n",
+ "# TemporarySubsystemManager is used to temporarily fix some variables to make sure\n",
+ "# we're debugging a square subsystem.\n",
+ "from pyomo.util.subsystems import TemporarySubsystemManager\n",
+ "\n",
+ "# Let's start with t=0. Really, we'd probably want to do this in a loop and try all time points.\n",
+ "t0 = model.fs.time.first()\n",
+ "t_block, inputs = get_subsystem_at_time(model, model.fs.time, t0)\n",
+ "# We'll temporarily fix the \"inputs\" to make sure we have a square system while debugging\n",
+ "with TemporarySubsystemManager(to_fix=inputs):\n",
+ " dt = DiagnosticsToolbox(t_block)\n",
+ " dt.report_structural_issues()\n",
+ " dt.display_underconstrained_set()\n",
+ " dt.display_overconstrained_set()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "These over and under-constrained subsystems aren't much smaller, but now the over-constrained system decomposes into 10 small, independent blocks. These should be easier to debug.\n",
+ "\n",
+ "## Debugging the over-constrained subsystem\n",
+ "\n",
+ "To debug the over-constrained subsystem, we look for a constraint that is not calculating any of the variables in the subsystem. The \"odd constraint out\" here seems to be the mass fraction sum, `sum_component_eqn`. This must \"solve for\" one of the mass fractions, which means one of the `material_holdup_calculation` equations must \"solve for\" particle density rather than mass fraction. If we want to see what variables are contained in one of these constraints, we can always `pprint` it:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model.fs.MB.solid_phase.properties[0, 0.9].sum_component_eqn.pprint()\n",
+ "model.fs.MB.solid_phase.material_holdup_calculation[0, 0.9, \"Sol\", \"Fe3O4\"].pprint()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If one of these `material_holdup_calculation` equations is solving for particle density, then that means that `density_particle_constraint` is not actually solving for density. Maybe `density_particle_constraint` is over-determining our system?"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model.fs.MB.solid_phase.properties[0, 0.9].density_particle_constraint.pprint()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "But this looks like a very reasonable constraint. After some thought, which admittedly requires some knowledge of the process we are modeling, we decide that the right approach is to make particle porosity a variable. We have assumed that porosity is constant, but this overconstrained subsystem is telling us that this assumption is not valid.\n",
+ "\n",
+ "### How did we figure this out? (OKAY TO SKIP)\n",
+ "Adding a variable (including by unfixing a parameter) to an over-constraining constraint will often remove that constraint from the over-constrained subsystem. But how did we know that this was the right thing to do? If you just care about using the diagnostics toolbox to extract as much information about a singularity as possible, you can skip this section. But if you are curious how we determined that particle porosity should not be constant, read on.\n",
+ "\n",
+ "`dens_mass_skeletal` is determined purely by the composition of solid, which is made up of Fe2O3, Fe3O4, and inert Ti2O3. We can view the `density_skeletal_constraint` as follows:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model.fs.MB.solid_phase.properties[0, 0.9].density_skeletal_constraint.pprint()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If we assume a constant particle porosity, this gives us a particle porosity that is also uniquely determined by the solid composition by the above `density_particle_constraint`:\n",
+ "```\n",
+ "dens_mass_particle = (1 - porosity) * dens_mass_skeletal\n",
+ "```\n",
+ "But the composition of the solid is determined by the (somewhat misnamed) `material_holdup_calculation` constraints. While the name of these constraints implies they \"calculate holdups,\" material holdups at $t=0$ are fixed as initial conditions (because holdups are the differential variables with respect to time in this model). At other time points, we assume that holdups are specified by differential and discretization equations of the model. This means that the `material_holdup_calculation` constraints actually calculate the solid phase mass fractions *from* the holdups. But as we hinted at above, the 4-by-4 system of holdup calculation constraints, `sum_component_eqn` (which simply constrains the sum of mass fractions to be one), mass fractions, and `dens_mass_particle`, uniquely solve for `dens_mass_particle` *as well as* the mass fractions. But if the holdup variables can be used to solve for the mass fractions, they *also* solve for `dens_mass_skeletal`. So both sides of `density_particle_constraint` are already uniquely determined! This implies that we don't need this constraint at all, but we also know that this constraint has to hold. Something has to give. With this in mind, we actually have several options for how to resolve this overspecification:\n",
+ "1. Remove `density_particle_constraint`. Then we would have `dens_mass_particle` and `dens_mass_skeletal`, with no relationship between them. This would leave us with a mathematically sound model, but with densities that contradict constant particle porosity that we have assumed (which is used elsewhere in the reaction rate calculation equations).\n",
+ "2. Remove the constraints that calculate skeletal density from composition.\n",
+ "3. Relax particle porosity from a parameter to a variable.\n",
+ "\n",
+ "Options 2 and 3 are equally valid. We've chosen option 3, meaning we assume that the particle \"evolves\" with a density that is well determined from its constituent species, rather than changing density to accommodate whatever mass it accumulates via reaction without altering its volume. This exercise should remind us that all mathematical modeling is somewhat of an art. In the process of choosing the \"least bad\" model, it is fairly easy to over or under-specify something by making the wrong combination of assumptions, and the Dulmage-Mendelsohn decomposition is a great tool for detecting when this has happened.\n",
+ "\n",
+ "## Debugging the under-constrained subsystem\n",
+ "\n",
+ "The under-constrained system does not decompose into independent subsystems, making it more difficult to debug. However, by inspection, we notice that the same constraints and variables seem to be repeated at each point in the length domain. For each point in space, the \"odd variable out\" seems to be the total flow rate `flow_mass`. Using some intuition about this particular process model, we may conclude that this variable should be calculated from the solid phase velocity, which is constant. We expect an equation that looks like\n",
+ "```\n",
+ "flow_mass == velocity * area * density\n",
+ "```\n",
+ "\n",
+ "But this equation isn't here... so we need to add it.\n",
+ "\n",
+ "# Fixing the model\n",
+ "\n",
+ "We'll start by creating a fresh copy of the model, so we don't accidentally rely on IPOPT's point of termination."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2 = make_model()\n",
+ "# Make the model square while we try to fix the structural singularity\n",
+ "model2.fs.MB.gas_phase.properties[:, 0].flow_mol.fix()\n",
+ "model2.fs.MB.solid_phase.properties[:, 1].flow_mass.fix()\n",
+ "model2.piecewise_constant_constraints.deactivate()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Adding a new particle porosity variable"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2.fs.MB.particle_porosity = pyo.Var(\n",
+ " model2.fs.time,\n",
+ " model2.fs.MB.length_domain,\n",
+ " initialize=model2.fs.solid_properties.particle_porosity.value,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now we need to replace the old particle porosity parameter with this new variable. Luckily, the old parameter is actually implemented as a fixed variable, so we can easily identify all the constraints it participates in with `IncidenceGraphInterface`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from pyomo.contrib.incidence_analysis import IncidenceGraphInterface\n",
+ "\n",
+ "igraph = IncidenceGraphInterface(model2, include_fixed=True)\n",
+ "porosity_param = model2.fs.solid_properties.particle_porosity\n",
+ "print(f\"Constraints containing {porosity_param.name}:\")\n",
+ "for con in igraph.get_adjacent_to(porosity_param):\n",
+ " print(f\" {con.name}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Particle porosity only appears in two constraints: the density constraint we saw above, and the reaction rate equation. We can replace particle porosity in these constraints using Pyomo's `replace_expressions` function:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from pyomo.core.expr import replace_expressions\n",
+ "\n",
+ "for t, x in model2.fs.time * model2.fs.MB.length_domain:\n",
+ " substitution_map = {id(porosity_param): model2.fs.MB.particle_porosity[t, x]}\n",
+ " sp = model2.fs.MB.solid_phase\n",
+ " cons = [\n",
+ " sp.properties[t, x].density_particle_constraint,\n",
+ " sp.reactions[t, x].gen_rate_expression[\"R1\"],\n",
+ " ]\n",
+ " for con in cons:\n",
+ " con.set_value(\n",
+ " replace_expressions(\n",
+ " con.expr, substitution_map, descend_into_named_expressions=True\n",
+ " )\n",
+ " )"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We have added a new `particle_porosity` variable, and are using it in the relevant locations. Now we can move on to adding the missing constraint.\n",
+ "\n",
+ "## Adding a new density-flow rate constraint"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "@model2.fs.MB.Constraint(model2.fs.time, model2.fs.MB.length_domain)\n",
+ "def density_flowrate_constraint(mb, t, x):\n",
+ " return (\n",
+ " mb.velocity_superficial_solid[t]\n",
+ " * mb.bed_area\n",
+ " * mb.solid_phase.properties[t, x].dens_mass_particle\n",
+ " == mb.solid_phase.properties[t, x].flow_mass\n",
+ " )"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Testing the new model\n",
+ "\n",
+ "Let's see if these changes have fixed our model."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Construct a new diagnostics toolbox\n",
+ "dt = DiagnosticsToolbox(model2)\n",
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The structural singularity seems to be gone! Let's unfix our degrees of freedom and see if we can solve."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2.fs.MB.gas_phase.properties[:, 0].flow_mol.unfix()\n",
+ "model2.fs.MB.gas_phase.properties[0, 0].flow_mol.fix()\n",
+ "model2.fs.MB.solid_phase.properties[:, 1].flow_mass.unfix()\n",
+ "model2.fs.MB.solid_phase.properties[0, 1].flow_mass.fix()\n",
+ "model2.piecewise_constant_constraints.activate()\n",
+ "\n",
+ "res = solver.solve(model2, tee=True)\n",
+ "print(f\"Converged successfully: {pyo.check_optimal_termination(res)}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This doesn't look much better. What's going on? I thought we just fixed the issue?\n",
+ "\n",
+ "# Debugging the model, take two\n",
+ "\n",
+ "Let's check the diagnostics toolbox for numerical issues."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2.fs.MB.gas_phase.properties[:, 0].flow_mol.fix()\n",
+ "model2.fs.MB.solid_phase.properties[:, 1].flow_mass.fix()\n",
+ "model2.piecewise_constant_constraints.deactivate()\n",
+ "dt.report_numerical_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Looks like we have \"parallel constraints\", which are another form of singularity. Let's follow the toolbox's advice to see what they are."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "dt.display_near_parallel_constraints()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "`density_flowrate_constraint` is the constraint that we added. What is `solid_super_vel`?"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2.fs.MB.solid_super_vel[0].pprint()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This is the same as the constraint we just added! Looks like that constraint already existed at the solid inlet. We can easily deactivate the new constraints at this point in the length domain:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2.fs.MB.density_flowrate_constraint[:, 1.0].deactivate();"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "But now we have removed constraints from a square model, and expect to have degrees of freedom. Let's see what the diagnostics toolbox has to say."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "dt = DiagnosticsToolbox(model2)\n",
+ "dt.report_structural_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "But this doesn't help us very much. We have some extraneous degrees of freedom, but with 8881 variables in the under-constrained subsystem, it will be difficult to tell what they are. After some thought (and model-specific intuition), we land on the conclusion that maybe we need to fix particle porosity at the solid inlet. Here, total flow rate is specified, and the `solid_super_vel` equation is using it to compute velocity. So we need `dens_mass_particle` to be known, which means we need `particle_porosity` to be fixed."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2.fs.MB.particle_porosity[:, 1.0].fix();"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let's run the diagnostics toolbox as a sanity check."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "dt = DiagnosticsToolbox(model2)\n",
+ "dt.report_structural_issues()\n",
+ "dt.report_numerical_issues()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Looks good! Now we can release our degrees of freedom and try to solve again."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model2.fs.MB.gas_phase.properties[:, 0].flow_mol.unfix()\n",
+ "model2.fs.MB.gas_phase.properties[0, 0].flow_mol.fix()\n",
+ "model2.fs.MB.solid_phase.properties[:, 1].flow_mass.unfix()\n",
+ "model2.fs.MB.solid_phase.properties[0, 1].flow_mass.fix()\n",
+ "model2.piecewise_constant_constraints.activate()\n",
+ "\n",
+ "res = solver.solve(model2, tee=True)\n",
+ "print(f\"Converged successfully: {pyo.check_optimal_termination(res)}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "It worked! For the simple optimization problem we have set up, this solve looks a lot more like what we expect.\n",
+ "\n",
+ "# Takeaways from this tutorial\n",
+ "What have we learned?\n",
+ "1. IPOPT using non-zero regularization coefficients hints at a singular Jacobian (especially when \"L\"/\"l\" diagnostic tags are present).\n",
+ "2. When this happens, start by calling `report_structural_issues` to check for a structural singularity. If this looks good, call `report_numerical_issues` to check for a numerical singularity.\n",
+ "3. When debugging a structural singularity, decomposing a problem into subsystems that each should be nonsingular (e.g. unit models or points in time) is very useful.\n",
+ "4. The solution to a structural singularity is often to relax a fixed parameter, add a constraint that was forgotten, remove a constraint that was redundant, or fix an extraneous degree of freedom.\n",
+ "5. Model-specific intuition is usually necessary to diagnose and fix modeling issues. (If you're an algorithm developer, learn about the models you're using! If you don't understand your models, you don't understand your algorithms!)\n",
+ "6. A modeling issue doesn't necessarily have a unique solution. This is especially true when the issue involves invalid assumptions.\n",
+ "7. Debugging is an iterative process — fixing one issue can introduce another."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# References\n",
+ "\n",
+ "[[1]] Okoli et al., \"A framework for the optimization of chemical looping combustion processes\". *Powder Tech*, 2020.\n",
+ "\n",
+ "[[2]] Parker and Biegler, \"Dynamic modeling and nonlinear model predictive control of a moving bed chemical looping combustion reactor\". *IFAC PapersOnline*, 2022.\n",
+ "\n",
+ "[[3]] Dulmage and Mendelsohn, \"Coverings of bipartite graphs\". *Can J. Math.*, 1958.\n",
+ "\n",
+ "[[4]] Pothen and Fan, \"Computing the block triangular form of a sparse matrix\". *ACM Trans. Math. Softw.*, 1990.\n",
+ "\n",
+ "[[5]] Parker et al., \"Applications of the Dulmage-Mendelsohn decomposition for debugging nonlinear optimization problems\". *Comp. Chem. Eng.*, 2023.\n",
+ "\n",
+ "[1]: https://www.sciencedirect.com/science/article/pii/S0032591019302803\n",
+ "[2]: https://www.sciencedirect.com/science/article/pii/S2405896322008825\n",
+ "[3]: https://www.cambridge.org/core/journals/canadian-journal-of-mathematics/article/coverings-of-bipartite-graphs/413735C5888AB542B92D0C4F402800B1\n",
+ "[4]: https://dl.acm.org/doi/10.1145/98267.98287\n",
+ "[5]: https://www.sciencedirect.com/science/article/pii/S0098135423002533\n"
+ ]
+ }
+ ],
+ "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.12.3"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 3
+}
\ No newline at end of file
diff --git a/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_costing.ipynb b/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_costing.ipynb
index 0b9c4081..656dc674 100644
--- a/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_costing.ipynb
+++ b/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_costing.ipynb
@@ -232,7 +232,7 @@
"source": [
"The costing module provides a `unit_mapping` dictionary linking generic unit model classes with recommended costing methods. In this example, StoichiometricReactor and Flash vessels utilize different vessel costing methods with similar arguments. The diameter and length attributes need to exist in order to cost vessel sizing and material requirements, and we add them if they don't exist already. The `unit_mapping` method provides an opportunity to automatically select the correct vessel orientation (vertical or horizontal) based on the unit type; passing a `StoichiometricReactor` or `PFR` class object will call the `cost_horizontal_vessel` method, while passing a `Flash` or `CSTR` class object will call the `cost_vertical_vessel` method.\n",
"\n",
- "All vessels are assigned costing succintly via a loop below - users may define each block individually if desired:"
+ "All vessels are assigned costing succinctly via a loop below - users may define each block individually if desired:"
]
},
{
@@ -273,8 +273,7 @@
" unit.length = Var(initialize=1, units=pyunits.m)\n",
" if hasattr(unit, \"volume\"): # if volume exists, set diameter from volume\n",
" unit.volume_eq = Constraint(\n",
- " expr=unit.volume[0]\n",
- " == unit.length * unit.diameter**2 * 0.25 * Constants.pi\n",
+ " expr=unit.volume[0] == unit.length * unit.diameter**2 * 0.25 * Constants.pi\n",
" )\n",
" else: # fix diameter directly\n",
" unit.diameter.fix(0.2214 * pyunits.m)\n",
diff --git a/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_costing_doc.ipynb b/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_costing_doc.ipynb
index 9339ebf2..0a6a239c 100644
--- a/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_costing_doc.ipynb
+++ b/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_costing_doc.ipynb
@@ -838,7 +838,7 @@
"source": [
"The costing module provides a `unit_mapping` dictionary linking generic unit model classes with recommended costing methods. In this example, StoichiometricReactor and Flash vessels utilize different vessel costing methods with similar arguments. The diameter and length attributes need to exist in order to cost vessel sizing and material requirements, and we add them if they don't exist already. The `unit_mapping` method provides an opportunity to automatically select the correct vessel orientation (vertical or horizontal) based on the unit type; passing a `StoichiometricReactor` or `PFR` class object will call the `cost_horizontal_vessel` method, while passing a `Flash` or `CSTR` class object will call the `cost_vertical_vessel` method.\n",
"\n",
- "All vessels are assigned costing succintly via a loop below - users may define each block individually if desired:"
+ "All vessels are assigned costing succinctly via a loop below - users may define each block individually if desired:"
]
},
{
@@ -879,8 +879,7 @@
" unit.length = Var(initialize=1, units=pyunits.m)\n",
" if hasattr(unit, \"volume\"): # if volume exists, set diameter from volume\n",
" unit.volume_eq = Constraint(\n",
- " expr=unit.volume[0]\n",
- " == unit.length * unit.diameter**2 * 0.25 * Constants.pi\n",
+ " expr=unit.volume[0] == unit.length * unit.diameter**2 * 0.25 * Constants.pi\n",
" )\n",
" else: # fix diameter directly\n",
" unit.diameter.fix(0.2214 * pyunits.m)\n",
@@ -1866,4 +1865,4 @@
},
"nbformat": 4,
"nbformat_minor": 3
-}
\ No newline at end of file
+}
diff --git a/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_costing_test.ipynb b/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_costing_test.ipynb
index 054f1c48..feceb84b 100644
--- a/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_costing_test.ipynb
+++ b/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_costing_test.ipynb
@@ -1,563 +1,562 @@
{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "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": [
- "\n",
- "# HDA Flowsheet Costing\n",
- "Maintainer: Brandon Paul \n",
- "Author: Brandon Paul \n",
- "Updated: 2023-06-01 \n",
- "\n",
- "\n",
- "## Note\n",
- "\n",
- "This example will demonstrate adding capital and operating costs to the two HDA examples, the basic [HDA with Flash](../tut/hda_flowsheet_solution_test.ipynb) and a comparison with the HDA with Distillation.\n",
- "\n",
- "\n",
- "## Learning outcomes\n",
- "\n",
- "- Import external pre-built steady-state flowsheets using the IDAES unit model library\n",
- "- Define and add costing blocks using the IDAES Process Costing Framework\n",
- "- Fomulate and solve a process economics optimization problem\n",
- " - Defining an objective function\n",
- " - Setting variable bounds\n",
- " - Adding additional constraints \n",
- "\n",
- "\n",
- "## Problem Statement\n",
- "\n",
- "Hydrodealkylation is a chemical reaction that often involves reacting\n",
- "an aromatic hydrocarbon in the presence of hydrogen gas to form a\n",
- "simpler aromatic hydrocarbon devoid of functional groups. In this\n",
- "example, toluene will be reacted with hydrogen gas at high temperatures\n",
- " to form benzene via the following reaction:\n",
- "\n",
- "**C6H5CH3 + H2 \u2192 C6H6 + CH4**\n",
- "\n",
- "\n",
- "This reaction is often accompanied by an equilibrium side reaction\n",
- "which forms diphenyl, which we will neglect for this example.\n",
- "\n",
- "This example is based on the 1967 AIChE Student Contest problem as\n",
- "present by Douglas, J.M., Chemical Design of Chemical Processes, 1988,\n",
- "McGraw-Hill.\n",
- "\n",
- "Users may refer to the prior examples linked at the top of this notebook for detailed process descriptions of the two HDA configurations. As before, the properties required for this module are defined in\n",
- "\n",
- "- `hda_ideal_VLE.py`\n",
- "- `idaes.models.properties.activity_coeff_models.BTX_activity_coeff_VLE`\n",
- "- `hda_reaction.py`\n",
- "\n",
- "Additionally, we will be importing externally-defined flowsheets for the two HDA configurations from\n",
- "\n",
- "- `hda_flowsheets_for_costing_notebook.py`"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Import and run HDA Flowsheets\n",
- "First, we will generate solved flowsheets for each HDA model. The external scripts build and set inputs for the flowsheets, initialize unit models and streams, and solve the flowsheets before returning the model objects. Note that the HDA flowsheets contain all unit models and stream connections, and no costing equations."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The flowsheet utilizes the Wegstein method to iteratively solve circular dependencies such as recycle streams, and is intended to approach a feasible solution. As such, the calls below will fail to converge after 3 iterations and pass to IPOPT to obtain an optimal solution as expected:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {
- "scrolled": true
- },
- "outputs": [],
- "source": [
- "# Source file for prebuilt flowsheets\n",
- "from hda_flowsheets_for_costing_notebook import hda_with_flash\n",
- "\n",
- "# Build hda model with second flash unit and return model object\n",
- "m = hda_with_flash(tee=True)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## IDAES Process Costing Framework\n",
- "IDAES provides a library of capital costing correlations based on those in the following source:\n",
- "\n",
- "*Process and Product Design Principles: Synthesis, Analysis, and Evaluation*. Seider, Seader, Lewin, Windagdo, 3rd Ed. John Wiley and Sons Chapter 22. Cost Accounting and Capital Cost Estimation 22.2 Cost Indexes and Capital Investment.\n",
- "\n",
- "Currently, IDAES supports calculation of capital costing for a wide array of unit operations, vesseel sizing and material properties, and specific unit options such as column tray types and heat exchanger configurations. Users may find further information on specific costing methods and options in the IDAES Process Costing Framework documentation (link pending).\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Add Operating Cost Equations\n",
- "Before adding capital costing blocks, we will add operating cost equations taken from the basic [HDA with Flash](../tut/hda_flowsheet_solution_test.ipynb) and the HDA with Distillation examples. The examples assume constant cooling and heating coefficients over an annual cost basis. The IDAES Generic Costing Framework does not currently support variable cost calculations."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Required imports\n",
- "from pyomo.environ import Expression\n",
- "\n",
- "# Operating costs for HDA with second flash (model m)\n",
- "m.fs.cooling_cost = Expression(\n",
- " expr=0.212e-7 * (-m.fs.F101.heat_duty[0]) + 0.212e-7 * (-m.fs.R101.heat_duty[0])\n",
- ")\n",
- "m.fs.heating_cost = Expression(\n",
- " expr=2.2e-7 * m.fs.H101.heat_duty[0] + 1.9e-7 * m.fs.F102.heat_duty[0]\n",
- ")\n",
- "m.fs.operating_cost = Expression(\n",
- " expr=(3600 * 24 * 365 * (m.fs.heating_cost + m.fs.cooling_cost))\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Add Capital Costing\n",
- "Below, we will add add capital costing blocks to the imported flowsheets and evaluate the economic impact of replacing the second Flash with a Distillation column. First, let's import and define the main flowsheet costing block:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Import costing methods - classes, heaters, vessels, compressors, columns\n",
- "from idaes.models.costing.SSLW import (\n",
- " SSLWCosting,\n",
- " SSLWCostingData,\n",
- ")\n",
- "from idaes.core import UnitModelCostingBlock\n",
- "\n",
- "# Costing block\n",
- "m.fs.costing = SSLWCosting()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Next, we will build the relevant costing blocks for the equipment we wish to cost. Note how the costing block, methods and flags are passed as arguments in the costing block call itself. Each unit model will have a single costing block, but each flowsheet model (m and n) will also have a single costing block for flowsheet-level properties.\n",
- "\n",
- "Users should note that IDAES costing methods support a wide array of heating sources (e.g. fired, steam boiler, hot water) and do not support direct capital costing of coolers. If users wish to cost Heater units acting as coolers, it is necessary to cost a \"dummy\" [0D shell and tube exchanger](https://idaes-pse.readthedocs.io/en/stable/reference_guides/model_libraries/generic/unit_models/heat_exchanger.html) with appropriate aliased hot stream properties and proper cooling water properties. This is not demonstrated here, as the HDA examples take advantage of Flash and Condenser operations to recover liquid product.\n",
- "\n",
- "Capital costing is independent of unit model connections, and building cost equations may be done piecewise in this fashion. Default options are passed explicitly to demonstrate proper syntax and usage. Now that all required properties are defined, let's cost our models connecting costing blocks, methods and unit models in each flowsheet."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Flexibility of Costing Block Definitions\n",
- "IDAES supports many ways to define batches of costing blocks, and several are shown in the example. Users may employ whichever method fits their modeling needs for explicit or concise code. In the code below, note how the unit model itself is never passed to the costing method; when the full model is executed, the costing block will automatically connect its parent block with child equation blocks.\n",
- "\n",
- "`Compressor` unit models with isothermal or adiabatic thermodynamics are too simple to cost and are therefore excluded from the economic analysis."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Let's define costing for the heater unit:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {},
- "outputs": [],
- "source": [
- "from idaes.models.costing.SSLW import (\n",
- " HeaterMaterial,\n",
- " HeaterSource,\n",
- ")\n",
- "\n",
- "# Costing for heater - m.fs.H101\n",
- "m.fs.H101.costing = UnitModelCostingBlock(\n",
- " flowsheet_costing_block=m.fs.costing,\n",
- " costing_method=SSLWCostingData.cost_fired_heater,\n",
- " costing_method_arguments={\n",
- " \"material_type\": HeaterMaterial.CarbonSteel,\n",
- " \"heat_source\": HeaterSource.Fuel,\n",
- " },\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The costing module provides a `unit_mapping` dictionary linking generic unit model classes with recommended costing methods. In this example, StoichiometricReactor and Flash vessels utilize different vessel costing methods with similar arguments. The diameter and length attributes need to exist in order to cost vessel sizing and material requirements, and we add them if they don't exist already. The `unit_mapping` method provides an opportunity to automatically select the correct vessel orientation (vertical or horizontal) based on the unit type; passing a `StoichiometricReactor` or `PFR` class object will call the `cost_horizontal_vessel` method, while passing a `Flash` or `CSTR` class object will call the `cost_vertical_vessel` method.\n",
- "\n",
- "All vessels are assigned costing succintly via a loop below - users may define each block individually if desired:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [],
- "source": [
- "from idaes.models.costing.SSLW import (\n",
- " VesselMaterial,\n",
- " TrayType,\n",
- " TrayMaterial,\n",
- ")\n",
- "\n",
- "from idaes.core.util.constants import Constants\n",
- "from pyomo.environ import Var, Constraint, units as pyunits, Param, value\n",
- "from idaes.models.unit_models import StoichiometricReactor, Flash\n",
- "\n",
- "# Map unit models to unit classes\n",
- "# Will pass to unit_mapping which calls costing methods based on unit class\n",
- "unit_class_mapping = {\n",
- " m.fs.R101: StoichiometricReactor,\n",
- " m.fs.F101: Flash,\n",
- " m.fs.F102: Flash,\n",
- "}\n",
- "\n",
- "# Costing for vessels - m.fs.R101, m.fs.F101, m.fs.F102\n",
- "\n",
- "# Loop over units\n",
- "for unit in [m.fs.R101, m.fs.F101, m.fs.F102]:\n",
- " # Get correct unit class for unit model\n",
- " unit_class = unit_class_mapping[unit]\n",
- "\n",
- " # Add dimension variables and constraint if they don't exist\n",
- " if not hasattr(unit, \"diameter\"):\n",
- " unit.diameter = Var(initialize=1, units=pyunits.m)\n",
- " if not hasattr(unit, \"length\"):\n",
- " unit.length = Var(initialize=1, units=pyunits.m)\n",
- " if hasattr(unit, \"volume\"): # if volume exists, set diameter from volume\n",
- " unit.volume_eq = Constraint(\n",
- " expr=unit.volume[0]\n",
- " == unit.length * unit.diameter**2 * 0.25 * Constants.pi\n",
- " )\n",
- " else: # fix diameter directly\n",
- " unit.diameter.fix(0.2214 * pyunits.m)\n",
- " # Either way, fix L/D to calculate L from D\n",
- " unit.L_over_D = Constraint(expr=unit.length == 3 * unit.diameter)\n",
- "\n",
- " # Define vessel costing\n",
- " unit.costing = UnitModelCostingBlock(\n",
- " flowsheet_costing_block=unit.parent_block().costing, # e.g. m.fs.R101.costing\n",
- " costing_method=SSLWCostingData.unit_mapping[\n",
- " unit_class\n",
- " ], # e.g. cost_vertical_vessel()\n",
- " costing_method_arguments={\n",
- " \"material_type\": VesselMaterial.CarbonSteel,\n",
- " \"shell_thickness\": 1.25 * pyunits.inch,\n",
- " },\n",
- " )"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Solve Flowsheet Costing Blocks\n",
- "Now, we may solve the full flowsheet for all costing blocks:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Eefine solver\n",
- "from idaes.core.solvers import get_solver\n",
- "\n",
- "solver = get_solver()\n",
- "\n",
- "# Check that the degrees of freedom is zero\n",
- "from idaes.core.util.model_statistics import degrees_of_freedom\n",
- "\n",
- "assert degrees_of_freedom(m) == 0"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Check physical units consistency, solve and check solver status\n",
- "from pyomo.environ import TerminationCondition\n",
- "from pyomo.util.check_units import assert_units_consistent\n",
- "\n",
- "assert_units_consistent(m)\n",
- "results = solver.solve(m, tee=True, symbolic_solver_labels=True)\n",
- "assert results.solver.termination_condition == TerminationCondition.optimal"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "For comparison, we will call and build the HDA flowsheet replacing the second `Flash` with a `TrayColumn` distillation unit model. The flowsheet costing occurs in the external script `hda_flowsheets_for_costing_notebook.py` and is not shown here:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "metadata": {
- "scrolled": true
- },
- "outputs": [],
- "source": [
- "from pyomo.common.log import LoggingIntercept\n",
- "import logging\n",
- "from io import StringIO\n",
- "\n",
- "stream = StringIO()\n",
- "with LoggingIntercept(stream, \"idaes\", logging.WARNING):\n",
- " # Source file for prebuilt flowsheets\n",
- " from hda_flowsheets_for_costing_notebook import hda_with_distillation\n",
- "\n",
- " # Build hda model with distillation column and return model object\n",
- " n = hda_with_distillation(tee=False)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Results Comparison and Visualization\n",
- "For the two flowsheets above, let's sum the total operating and capital costs of each scenario. We will display overall process economics results and compare the two flowsheets:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Imports and data gathering\n",
- "from matplotlib import pyplot as plt\n",
- "\n",
- "plt.style.use(\"dark_background\") # if using browser in dark mode, uncomment this line\n",
- "import numpy as np\n",
- "import pandas as pd\n",
- "\n",
- "# Automatically get units that we costed - this will exclude C101 for both flowsheets\n",
- "\n",
- "two_flash_unitlist = [\n",
- " getattr(m.fs, unit) for unit in dir(m.fs) if hasattr(getattr(m.fs, unit), \"costing\")\n",
- "]\n",
- "distillation_unitlist = [\n",
- " getattr(n.fs, unit) for unit in dir(n.fs) if hasattr(getattr(n.fs, unit), \"costing\")\n",
- "]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Compare equipment purchase costs (actual capital costs)\n",
- "\n",
- "two_flash_capcost = {\n",
- " unit.name: value(unit.costing.capital_cost / 1e3) for unit in two_flash_unitlist\n",
- "}\n",
- "distillation_capcost = {\n",
- " unit.name: value(unit.costing.capital_cost / 1e3) for unit in distillation_unitlist\n",
- "}\n",
- "\n",
- "two_flash_capdf = pd.DataFrame(\n",
- " list(two_flash_capcost.items()), columns=[\"Equipment\", \"Two Flash\"]\n",
- ").set_index(\"Equipment\")\n",
- "distillation_capdf = pd.DataFrame(\n",
- " list(distillation_capcost.items()), columns=[\"Equipment\", \"Distillation\"]\n",
- ").set_index(\"Equipment\")\n",
- "\n",
- "# Add dataframes, merge same indices, replace NaNs with 0s, and transpose\n",
- "capcosts = two_flash_capdf.add(distillation_capdf, fill_value=0).fillna(0).transpose()\n",
- "\n",
- "# Sort according to an easier order to view\n",
- "capcosts = capcosts[[\"fs.H101\", \"fs.R101\", \"fs.F101\", \"fs.F102\", \"fs.D101\", \"fs.H102\"]]\n",
- "\n",
- "print(\"Costs in $1000:\")\n",
- "display(capcosts) # view dataframe before plotting\n",
- "\n",
- "capplot = capcosts.plot(\n",
- " kind=\"bar\", stacked=True, title=\"HDA Total Capital Costs\", ylabel=\"$1000\"\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 11,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Compare operating costs (per year)\n",
- "\n",
- "two_flash_opcost = {\n",
- " \"cooling\": value(3600 * 24 * 365 * m.fs.cooling_cost / 1e3),\n",
- " \"heating\": value(3600 * 24 * 365 * m.fs.heating_cost / 1e3),\n",
- "}\n",
- "distillation_opcost = {\n",
- " \"cooling\": value(3600 * 24 * 365 * n.fs.cooling_cost / 1e3),\n",
- " \"heating\": value(3600 * 24 * 365 * n.fs.heating_cost / 1e3),\n",
- "}\n",
- "\n",
- "two_flash_opdf = pd.DataFrame(\n",
- " list(two_flash_opcost.items()), columns=[\"Utilities\", \"Two Flash\"]\n",
- ").set_index(\"Utilities\")\n",
- "distillation_opdf = pd.DataFrame(\n",
- " list(distillation_opcost.items()), columns=[\"Utilities\", \"Distillation\"]\n",
- ").set_index(\"Utilities\")\n",
- "\n",
- "# Add dataframes, merge same indices, replace NaNs with 0s, and transpose\n",
- "opcosts = two_flash_opdf.add(distillation_opdf, fill_value=0).fillna(0).transpose()\n",
- "\n",
- "print(\"Costs in $1000:\")\n",
- "display(opcosts) # view dataframe before plotting\n",
- "\n",
- "opplot = opcosts.plot(\n",
- " kind=\"bar\", stacked=True, title=\"HDA Operating Costs\", ylabel=\"$1000/year\"\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 12,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Compare total costs (capital costs and operating costs)\n",
- "\n",
- "two_flash_totcost = {\n",
- " \"capital\": sum(two_flash_capcost[idx] for idx in two_flash_capcost),\n",
- " \"operating\": value(m.fs.operating_cost) / 1e3,\n",
- "}\n",
- "distillation_totcost = {\n",
- " \"capital\": sum(distillation_capcost[idx] for idx in distillation_capcost),\n",
- " \"operating\": value(n.fs.operating_cost) / 1e3,\n",
- "}\n",
- "\n",
- "two_flash_totdf = pd.DataFrame(\n",
- " list(two_flash_totcost.items()), columns=[\"Costs\", \"Two Flash\"]\n",
- ").set_index(\"Costs\")\n",
- "distillation_totdf = pd.DataFrame(\n",
- " list(distillation_totcost.items()), columns=[\"Costs\", \"Distillation\"]\n",
- ").set_index(\"Costs\")\n",
- "\n",
- "# Add dataframes, merge same indices, replace NaNs with 0s, and transpose\n",
- "totcosts = two_flash_totdf.add(distillation_totdf, fill_value=0).fillna(0).transpose()\n",
- "\n",
- "print(\"Costs in $1000:\")\n",
- "display(totcosts) # view dataframe before plotting\n",
- "\n",
- "totplot = totcosts.plot(\n",
- " kind=\"bar\", stacked=True, title=\"HDA Total Plant Cost (TPC)\", ylabel=\"$1000/year\"\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Finally, let's compare the total costs on a production basis. This will account for the greater efficiency provided by the distillation column relative to the less-expensive second flash unit:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 13,
- "metadata": {},
- "outputs": [],
- "source": [
- "two_flash_cost = value(1e3 * sum(two_flash_totcost[idx] for idx in two_flash_totcost))\n",
- "two_flash_prod = value(\n",
- " m.fs.F102.vap_outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"] * 365 * 24 * 3600\n",
- ")\n",
- "distillation_cost = value(\n",
- " 1e3 * sum(distillation_totcost[idx] for idx in distillation_totcost)\n",
- ")\n",
- "distillation_prod = value(n.fs.D101.condenser.distillate.flow_mol[0] * 365 * 24 * 3600)\n",
- "\n",
- "print(\n",
- " f\"Two flash case over one year: ${two_flash_cost/1e3:0.0f}K / {two_flash_prod/1e3:0.0f} kmol benzene = ${two_flash_cost/(two_flash_prod/1e3):0.2f} per kmol benzene produced\"\n",
- ")\n",
- "print(\n",
- " f\"Distillation case over one year: ${distillation_cost/1e3:0.0f}K / {distillation_prod/1e3:0.0f} kmol benzene = ${distillation_cost/(distillation_prod/1e3):0.2f} per kmol benzene produced\"\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Summary\n",
- "In this example, IDAES Process Costing Framework methods were applied to two HDA flowsheets for capital cost estimation. The costing blocks calls showcased multiple methods to define unit costing, demonstrating the flexibility and best practice of the costing framework. In the basic examples, the two-flash HDA did not include costing and the distillation HDA estimated a reactor capital cost comprising 3.3% of the total plant cost (TPC). With more rigorous costing, IDAES obtained total capital costs of 8.5% TPC (two flash HDA) and 9.6% (distillation HDA) and better modeled the impact of equipment cost on process economics. As printed above, the IDAES Process Costing Framework confirmed that replacing the second flash drum with a distillation column results in increased equipment costs, increased production and decreased cost per unit product."
- ]
- }
- ],
- "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.8.12"
- }
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "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",
+ "###############################################################################"
+ ]
},
- "nbformat": 4,
- "nbformat_minor": 3
-}
\ No newline at end of file
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "# HDA Flowsheet Costing\n",
+ "Maintainer: Brandon Paul \n",
+ "Author: Brandon Paul \n",
+ "Updated: 2023-06-01 \n",
+ "\n",
+ "\n",
+ "## Note\n",
+ "\n",
+ "This example will demonstrate adding capital and operating costs to the two HDA examples, the basic [HDA with Flash](../tut/hda_flowsheet_solution_test.ipynb) and a comparison with the HDA with Distillation.\n",
+ "\n",
+ "\n",
+ "## Learning outcomes\n",
+ "\n",
+ "- Import external pre-built steady-state flowsheets using the IDAES unit model library\n",
+ "- Define and add costing blocks using the IDAES Process Costing Framework\n",
+ "- Fomulate and solve a process economics optimization problem\n",
+ " - Defining an objective function\n",
+ " - Setting variable bounds\n",
+ " - Adding additional constraints \n",
+ "\n",
+ "\n",
+ "## Problem Statement\n",
+ "\n",
+ "Hydrodealkylation is a chemical reaction that often involves reacting\n",
+ "an aromatic hydrocarbon in the presence of hydrogen gas to form a\n",
+ "simpler aromatic hydrocarbon devoid of functional groups. In this\n",
+ "example, toluene will be reacted with hydrogen gas at high temperatures\n",
+ " to form benzene via the following reaction:\n",
+ "\n",
+ "**C6H5CH3 + H2 → C6H6 + CH4**\n",
+ "\n",
+ "\n",
+ "This reaction is often accompanied by an equilibrium side reaction\n",
+ "which forms diphenyl, which we will neglect for this example.\n",
+ "\n",
+ "This example is based on the 1967 AIChE Student Contest problem as\n",
+ "present by Douglas, J.M., Chemical Design of Chemical Processes, 1988,\n",
+ "McGraw-Hill.\n",
+ "\n",
+ "Users may refer to the prior examples linked at the top of this notebook for detailed process descriptions of the two HDA configurations. As before, the properties required for this module are defined in\n",
+ "\n",
+ "- `hda_ideal_VLE.py`\n",
+ "- `idaes.models.properties.activity_coeff_models.BTX_activity_coeff_VLE`\n",
+ "- `hda_reaction.py`\n",
+ "\n",
+ "Additionally, we will be importing externally-defined flowsheets for the two HDA configurations from\n",
+ "\n",
+ "- `hda_flowsheets_for_costing_notebook.py`"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Import and run HDA Flowsheets\n",
+ "First, we will generate solved flowsheets for each HDA model. The external scripts build and set inputs for the flowsheets, initialize unit models and streams, and solve the flowsheets before returning the model objects. Note that the HDA flowsheets contain all unit models and stream connections, and no costing equations."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The flowsheet utilizes the Wegstein method to iteratively solve circular dependencies such as recycle streams, and is intended to approach a feasible solution. As such, the calls below will fail to converge after 3 iterations and pass to IPOPT to obtain an optimal solution as expected:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {
+ "scrolled": true
+ },
+ "outputs": [],
+ "source": [
+ "# Source file for prebuilt flowsheets\n",
+ "from hda_flowsheets_for_costing_notebook import hda_with_flash\n",
+ "\n",
+ "# Build hda model with second flash unit and return model object\n",
+ "m = hda_with_flash(tee=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## IDAES Process Costing Framework\n",
+ "IDAES provides a library of capital costing correlations based on those in the following source:\n",
+ "\n",
+ "*Process and Product Design Principles: Synthesis, Analysis, and Evaluation*. Seider, Seader, Lewin, Windagdo, 3rd Ed. John Wiley and Sons Chapter 22. Cost Accounting and Capital Cost Estimation 22.2 Cost Indexes and Capital Investment.\n",
+ "\n",
+ "Currently, IDAES supports calculation of capital costing for a wide array of unit operations, vesseel sizing and material properties, and specific unit options such as column tray types and heat exchanger configurations. Users may find further information on specific costing methods and options in the IDAES Process Costing Framework documentation (link pending).\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Add Operating Cost Equations\n",
+ "Before adding capital costing blocks, we will add operating cost equations taken from the basic [HDA with Flash](../tut/hda_flowsheet_solution_test.ipynb) and the HDA with Distillation examples. The examples assume constant cooling and heating coefficients over an annual cost basis. The IDAES Generic Costing Framework does not currently support variable cost calculations."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Required imports\n",
+ "from pyomo.environ import Expression\n",
+ "\n",
+ "# Operating costs for HDA with second flash (model m)\n",
+ "m.fs.cooling_cost = Expression(\n",
+ " expr=0.212e-7 * (-m.fs.F101.heat_duty[0]) + 0.212e-7 * (-m.fs.R101.heat_duty[0])\n",
+ ")\n",
+ "m.fs.heating_cost = Expression(\n",
+ " expr=2.2e-7 * m.fs.H101.heat_duty[0] + 1.9e-7 * m.fs.F102.heat_duty[0]\n",
+ ")\n",
+ "m.fs.operating_cost = Expression(\n",
+ " expr=(3600 * 24 * 365 * (m.fs.heating_cost + m.fs.cooling_cost))\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Add Capital Costing\n",
+ "Below, we will add add capital costing blocks to the imported flowsheets and evaluate the economic impact of replacing the second Flash with a Distillation column. First, let's import and define the main flowsheet costing block:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Import costing methods - classes, heaters, vessels, compressors, columns\n",
+ "from idaes.models.costing.SSLW import (\n",
+ " SSLWCosting,\n",
+ " SSLWCostingData,\n",
+ ")\n",
+ "from idaes.core import UnitModelCostingBlock\n",
+ "\n",
+ "# Costing block\n",
+ "m.fs.costing = SSLWCosting()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Next, we will build the relevant costing blocks for the equipment we wish to cost. Note how the costing block, methods and flags are passed as arguments in the costing block call itself. Each unit model will have a single costing block, but each flowsheet model (m and n) will also have a single costing block for flowsheet-level properties.\n",
+ "\n",
+ "Users should note that IDAES costing methods support a wide array of heating sources (e.g. fired, steam boiler, hot water) and do not support direct capital costing of coolers. If users wish to cost Heater units acting as coolers, it is necessary to cost a \"dummy\" [0D shell and tube exchanger](https://idaes-pse.readthedocs.io/en/stable/reference_guides/model_libraries/generic/unit_models/heat_exchanger.html) with appropriate aliased hot stream properties and proper cooling water properties. This is not demonstrated here, as the HDA examples take advantage of Flash and Condenser operations to recover liquid product.\n",
+ "\n",
+ "Capital costing is independent of unit model connections, and building cost equations may be done piecewise in this fashion. Default options are passed explicitly to demonstrate proper syntax and usage. Now that all required properties are defined, let's cost our models connecting costing blocks, methods and unit models in each flowsheet."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Flexibility of Costing Block Definitions\n",
+ "IDAES supports many ways to define batches of costing blocks, and several are shown in the example. Users may employ whichever method fits their modeling needs for explicit or concise code. In the code below, note how the unit model itself is never passed to the costing method; when the full model is executed, the costing block will automatically connect its parent block with child equation blocks.\n",
+ "\n",
+ "`Compressor` unit models with isothermal or adiabatic thermodynamics are too simple to cost and are therefore excluded from the economic analysis."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let's define costing for the heater unit:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from idaes.models.costing.SSLW import (\n",
+ " HeaterMaterial,\n",
+ " HeaterSource,\n",
+ ")\n",
+ "\n",
+ "# Costing for heater - m.fs.H101\n",
+ "m.fs.H101.costing = UnitModelCostingBlock(\n",
+ " flowsheet_costing_block=m.fs.costing,\n",
+ " costing_method=SSLWCostingData.cost_fired_heater,\n",
+ " costing_method_arguments={\n",
+ " \"material_type\": HeaterMaterial.CarbonSteel,\n",
+ " \"heat_source\": HeaterSource.Fuel,\n",
+ " },\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The costing module provides a `unit_mapping` dictionary linking generic unit model classes with recommended costing methods. In this example, StoichiometricReactor and Flash vessels utilize different vessel costing methods with similar arguments. The diameter and length attributes need to exist in order to cost vessel sizing and material requirements, and we add them if they don't exist already. The `unit_mapping` method provides an opportunity to automatically select the correct vessel orientation (vertical or horizontal) based on the unit type; passing a `StoichiometricReactor` or `PFR` class object will call the `cost_horizontal_vessel` method, while passing a `Flash` or `CSTR` class object will call the `cost_vertical_vessel` method.\n",
+ "\n",
+ "All vessels are assigned costing succinctly via a loop below - users may define each block individually if desired:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from idaes.models.costing.SSLW import (\n",
+ " VesselMaterial,\n",
+ " TrayType,\n",
+ " TrayMaterial,\n",
+ ")\n",
+ "\n",
+ "from idaes.core.util.constants import Constants\n",
+ "from pyomo.environ import Var, Constraint, units as pyunits, Param, value\n",
+ "from idaes.models.unit_models import StoichiometricReactor, Flash\n",
+ "\n",
+ "# Map unit models to unit classes\n",
+ "# Will pass to unit_mapping which calls costing methods based on unit class\n",
+ "unit_class_mapping = {\n",
+ " m.fs.R101: StoichiometricReactor,\n",
+ " m.fs.F101: Flash,\n",
+ " m.fs.F102: Flash,\n",
+ "}\n",
+ "\n",
+ "# Costing for vessels - m.fs.R101, m.fs.F101, m.fs.F102\n",
+ "\n",
+ "# Loop over units\n",
+ "for unit in [m.fs.R101, m.fs.F101, m.fs.F102]:\n",
+ " # Get correct unit class for unit model\n",
+ " unit_class = unit_class_mapping[unit]\n",
+ "\n",
+ " # Add dimension variables and constraint if they don't exist\n",
+ " if not hasattr(unit, \"diameter\"):\n",
+ " unit.diameter = Var(initialize=1, units=pyunits.m)\n",
+ " if not hasattr(unit, \"length\"):\n",
+ " unit.length = Var(initialize=1, units=pyunits.m)\n",
+ " if hasattr(unit, \"volume\"): # if volume exists, set diameter from volume\n",
+ " unit.volume_eq = Constraint(\n",
+ " expr=unit.volume[0] == unit.length * unit.diameter**2 * 0.25 * Constants.pi\n",
+ " )\n",
+ " else: # fix diameter directly\n",
+ " unit.diameter.fix(0.2214 * pyunits.m)\n",
+ " # Either way, fix L/D to calculate L from D\n",
+ " unit.L_over_D = Constraint(expr=unit.length == 3 * unit.diameter)\n",
+ "\n",
+ " # Define vessel costing\n",
+ " unit.costing = UnitModelCostingBlock(\n",
+ " flowsheet_costing_block=unit.parent_block().costing, # e.g. m.fs.R101.costing\n",
+ " costing_method=SSLWCostingData.unit_mapping[\n",
+ " unit_class\n",
+ " ], # e.g. cost_vertical_vessel()\n",
+ " costing_method_arguments={\n",
+ " \"material_type\": VesselMaterial.CarbonSteel,\n",
+ " \"shell_thickness\": 1.25 * pyunits.inch,\n",
+ " },\n",
+ " )"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Solve Flowsheet Costing Blocks\n",
+ "Now, we may solve the full flowsheet for all costing blocks:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Eefine solver\n",
+ "from idaes.core.solvers import get_solver\n",
+ "\n",
+ "solver = get_solver()\n",
+ "\n",
+ "# Check that the degrees of freedom is zero\n",
+ "from idaes.core.util.model_statistics import degrees_of_freedom\n",
+ "\n",
+ "assert degrees_of_freedom(m) == 0"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Check physical units consistency, solve and check solver status\n",
+ "from pyomo.environ import TerminationCondition\n",
+ "from pyomo.util.check_units import assert_units_consistent\n",
+ "\n",
+ "assert_units_consistent(m)\n",
+ "results = solver.solve(m, tee=True, symbolic_solver_labels=True)\n",
+ "assert results.solver.termination_condition == TerminationCondition.optimal"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "For comparison, we will call and build the HDA flowsheet replacing the second `Flash` with a `TrayColumn` distillation unit model. The flowsheet costing occurs in the external script `hda_flowsheets_for_costing_notebook.py` and is not shown here:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {
+ "scrolled": true
+ },
+ "outputs": [],
+ "source": [
+ "from pyomo.common.log import LoggingIntercept\n",
+ "import logging\n",
+ "from io import StringIO\n",
+ "\n",
+ "stream = StringIO()\n",
+ "with LoggingIntercept(stream, \"idaes\", logging.WARNING):\n",
+ " # Source file for prebuilt flowsheets\n",
+ " from hda_flowsheets_for_costing_notebook import hda_with_distillation\n",
+ "\n",
+ " # Build hda model with distillation column and return model object\n",
+ " n = hda_with_distillation(tee=False)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Results Comparison and Visualization\n",
+ "For the two flowsheets above, let's sum the total operating and capital costs of each scenario. We will display overall process economics results and compare the two flowsheets:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Imports and data gathering\n",
+ "from matplotlib import pyplot as plt\n",
+ "\n",
+ "plt.style.use(\"dark_background\") # if using browser in dark mode, uncomment this line\n",
+ "import numpy as np\n",
+ "import pandas as pd\n",
+ "\n",
+ "# Automatically get units that we costed - this will exclude C101 for both flowsheets\n",
+ "\n",
+ "two_flash_unitlist = [\n",
+ " getattr(m.fs, unit) for unit in dir(m.fs) if hasattr(getattr(m.fs, unit), \"costing\")\n",
+ "]\n",
+ "distillation_unitlist = [\n",
+ " getattr(n.fs, unit) for unit in dir(n.fs) if hasattr(getattr(n.fs, unit), \"costing\")\n",
+ "]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Compare equipment purchase costs (actual capital costs)\n",
+ "\n",
+ "two_flash_capcost = {\n",
+ " unit.name: value(unit.costing.capital_cost / 1e3) for unit in two_flash_unitlist\n",
+ "}\n",
+ "distillation_capcost = {\n",
+ " unit.name: value(unit.costing.capital_cost / 1e3) for unit in distillation_unitlist\n",
+ "}\n",
+ "\n",
+ "two_flash_capdf = pd.DataFrame(\n",
+ " list(two_flash_capcost.items()), columns=[\"Equipment\", \"Two Flash\"]\n",
+ ").set_index(\"Equipment\")\n",
+ "distillation_capdf = pd.DataFrame(\n",
+ " list(distillation_capcost.items()), columns=[\"Equipment\", \"Distillation\"]\n",
+ ").set_index(\"Equipment\")\n",
+ "\n",
+ "# Add dataframes, merge same indices, replace NaNs with 0s, and transpose\n",
+ "capcosts = two_flash_capdf.add(distillation_capdf, fill_value=0).fillna(0).transpose()\n",
+ "\n",
+ "# Sort according to an easier order to view\n",
+ "capcosts = capcosts[[\"fs.H101\", \"fs.R101\", \"fs.F101\", \"fs.F102\", \"fs.D101\", \"fs.H102\"]]\n",
+ "\n",
+ "print(\"Costs in $1000:\")\n",
+ "display(capcosts) # view dataframe before plotting\n",
+ "\n",
+ "capplot = capcosts.plot(\n",
+ " kind=\"bar\", stacked=True, title=\"HDA Total Capital Costs\", ylabel=\"$1000\"\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Compare operating costs (per year)\n",
+ "\n",
+ "two_flash_opcost = {\n",
+ " \"cooling\": value(3600 * 24 * 365 * m.fs.cooling_cost / 1e3),\n",
+ " \"heating\": value(3600 * 24 * 365 * m.fs.heating_cost / 1e3),\n",
+ "}\n",
+ "distillation_opcost = {\n",
+ " \"cooling\": value(3600 * 24 * 365 * n.fs.cooling_cost / 1e3),\n",
+ " \"heating\": value(3600 * 24 * 365 * n.fs.heating_cost / 1e3),\n",
+ "}\n",
+ "\n",
+ "two_flash_opdf = pd.DataFrame(\n",
+ " list(two_flash_opcost.items()), columns=[\"Utilities\", \"Two Flash\"]\n",
+ ").set_index(\"Utilities\")\n",
+ "distillation_opdf = pd.DataFrame(\n",
+ " list(distillation_opcost.items()), columns=[\"Utilities\", \"Distillation\"]\n",
+ ").set_index(\"Utilities\")\n",
+ "\n",
+ "# Add dataframes, merge same indices, replace NaNs with 0s, and transpose\n",
+ "opcosts = two_flash_opdf.add(distillation_opdf, fill_value=0).fillna(0).transpose()\n",
+ "\n",
+ "print(\"Costs in $1000:\")\n",
+ "display(opcosts) # view dataframe before plotting\n",
+ "\n",
+ "opplot = opcosts.plot(\n",
+ " kind=\"bar\", stacked=True, title=\"HDA Operating Costs\", ylabel=\"$1000/year\"\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Compare total costs (capital costs and operating costs)\n",
+ "\n",
+ "two_flash_totcost = {\n",
+ " \"capital\": sum(two_flash_capcost[idx] for idx in two_flash_capcost),\n",
+ " \"operating\": value(m.fs.operating_cost) / 1e3,\n",
+ "}\n",
+ "distillation_totcost = {\n",
+ " \"capital\": sum(distillation_capcost[idx] for idx in distillation_capcost),\n",
+ " \"operating\": value(n.fs.operating_cost) / 1e3,\n",
+ "}\n",
+ "\n",
+ "two_flash_totdf = pd.DataFrame(\n",
+ " list(two_flash_totcost.items()), columns=[\"Costs\", \"Two Flash\"]\n",
+ ").set_index(\"Costs\")\n",
+ "distillation_totdf = pd.DataFrame(\n",
+ " list(distillation_totcost.items()), columns=[\"Costs\", \"Distillation\"]\n",
+ ").set_index(\"Costs\")\n",
+ "\n",
+ "# Add dataframes, merge same indices, replace NaNs with 0s, and transpose\n",
+ "totcosts = two_flash_totdf.add(distillation_totdf, fill_value=0).fillna(0).transpose()\n",
+ "\n",
+ "print(\"Costs in $1000:\")\n",
+ "display(totcosts) # view dataframe before plotting\n",
+ "\n",
+ "totplot = totcosts.plot(\n",
+ " kind=\"bar\", stacked=True, title=\"HDA Total Plant Cost (TPC)\", ylabel=\"$1000/year\"\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Finally, let's compare the total costs on a production basis. This will account for the greater efficiency provided by the distillation column relative to the less-expensive second flash unit:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "two_flash_cost = value(1e3 * sum(two_flash_totcost[idx] for idx in two_flash_totcost))\n",
+ "two_flash_prod = value(\n",
+ " m.fs.F102.vap_outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"] * 365 * 24 * 3600\n",
+ ")\n",
+ "distillation_cost = value(\n",
+ " 1e3 * sum(distillation_totcost[idx] for idx in distillation_totcost)\n",
+ ")\n",
+ "distillation_prod = value(n.fs.D101.condenser.distillate.flow_mol[0] * 365 * 24 * 3600)\n",
+ "\n",
+ "print(\n",
+ " f\"Two flash case over one year: ${two_flash_cost/1e3:0.0f}K / {two_flash_prod/1e3:0.0f} kmol benzene = ${two_flash_cost/(two_flash_prod/1e3):0.2f} per kmol benzene produced\"\n",
+ ")\n",
+ "print(\n",
+ " f\"Distillation case over one year: ${distillation_cost/1e3:0.0f}K / {distillation_prod/1e3:0.0f} kmol benzene = ${distillation_cost/(distillation_prod/1e3):0.2f} per kmol benzene produced\"\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Summary\n",
+ "In this example, IDAES Process Costing Framework methods were applied to two HDA flowsheets for capital cost estimation. The costing blocks calls showcased multiple methods to define unit costing, demonstrating the flexibility and best practice of the costing framework. In the basic examples, the two-flash HDA did not include costing and the distillation HDA estimated a reactor capital cost comprising 3.3% of the total plant cost (TPC). With more rigorous costing, IDAES obtained total capital costs of 8.5% TPC (two flash HDA) and 9.6% (distillation HDA) and better modeled the impact of equipment cost on process economics. As printed above, the IDAES Process Costing Framework confirmed that replacing the second flash drum with a distillation column results in increased equipment costs, increased production and decreased cost per unit product."
+ ]
+ }
+ ],
+ "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.8.12"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 3
+}
diff --git a/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_costing_usr.ipynb b/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_costing_usr.ipynb
index 5d73b18b..8c04460d 100644
--- a/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_costing_usr.ipynb
+++ b/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_costing_usr.ipynb
@@ -1,563 +1,562 @@
{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "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": [
- "\n",
- "# HDA Flowsheet Costing\n",
- "Maintainer: Brandon Paul \n",
- "Author: Brandon Paul \n",
- "Updated: 2023-06-01 \n",
- "\n",
- "\n",
- "## Note\n",
- "\n",
- "This example will demonstrate adding capital and operating costs to the two HDA examples, the basic [HDA with Flash](../tut/hda_flowsheet_solution_usr.ipynb) and a comparison with the HDA with Distillation.\n",
- "\n",
- "\n",
- "## Learning outcomes\n",
- "\n",
- "- Import external pre-built steady-state flowsheets using the IDAES unit model library\n",
- "- Define and add costing blocks using the IDAES Process Costing Framework\n",
- "- Fomulate and solve a process economics optimization problem\n",
- " - Defining an objective function\n",
- " - Setting variable bounds\n",
- " - Adding additional constraints \n",
- "\n",
- "\n",
- "## Problem Statement\n",
- "\n",
- "Hydrodealkylation is a chemical reaction that often involves reacting\n",
- "an aromatic hydrocarbon in the presence of hydrogen gas to form a\n",
- "simpler aromatic hydrocarbon devoid of functional groups. In this\n",
- "example, toluene will be reacted with hydrogen gas at high temperatures\n",
- " to form benzene via the following reaction:\n",
- "\n",
- "**C6H5CH3 + H2 \u2192 C6H6 + CH4**\n",
- "\n",
- "\n",
- "This reaction is often accompanied by an equilibrium side reaction\n",
- "which forms diphenyl, which we will neglect for this example.\n",
- "\n",
- "This example is based on the 1967 AIChE Student Contest problem as\n",
- "present by Douglas, J.M., Chemical Design of Chemical Processes, 1988,\n",
- "McGraw-Hill.\n",
- "\n",
- "Users may refer to the prior examples linked at the top of this notebook for detailed process descriptions of the two HDA configurations. As before, the properties required for this module are defined in\n",
- "\n",
- "- `hda_ideal_VLE.py`\n",
- "- `idaes.models.properties.activity_coeff_models.BTX_activity_coeff_VLE`\n",
- "- `hda_reaction.py`\n",
- "\n",
- "Additionally, we will be importing externally-defined flowsheets for the two HDA configurations from\n",
- "\n",
- "- `hda_flowsheets_for_costing_notebook.py`"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Import and run HDA Flowsheets\n",
- "First, we will generate solved flowsheets for each HDA model. The external scripts build and set inputs for the flowsheets, initialize unit models and streams, and solve the flowsheets before returning the model objects. Note that the HDA flowsheets contain all unit models and stream connections, and no costing equations."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The flowsheet utilizes the Wegstein method to iteratively solve circular dependencies such as recycle streams, and is intended to approach a feasible solution. As such, the calls below will fail to converge after 3 iterations and pass to IPOPT to obtain an optimal solution as expected:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {
- "scrolled": true
- },
- "outputs": [],
- "source": [
- "# Source file for prebuilt flowsheets\n",
- "from hda_flowsheets_for_costing_notebook import hda_with_flash\n",
- "\n",
- "# Build hda model with second flash unit and return model object\n",
- "m = hda_with_flash(tee=True)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## IDAES Process Costing Framework\n",
- "IDAES provides a library of capital costing correlations based on those in the following source:\n",
- "\n",
- "*Process and Product Design Principles: Synthesis, Analysis, and Evaluation*. Seider, Seader, Lewin, Windagdo, 3rd Ed. John Wiley and Sons Chapter 22. Cost Accounting and Capital Cost Estimation 22.2 Cost Indexes and Capital Investment.\n",
- "\n",
- "Currently, IDAES supports calculation of capital costing for a wide array of unit operations, vesseel sizing and material properties, and specific unit options such as column tray types and heat exchanger configurations. Users may find further information on specific costing methods and options in the IDAES Process Costing Framework documentation (link pending).\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Add Operating Cost Equations\n",
- "Before adding capital costing blocks, we will add operating cost equations taken from the basic [HDA with Flash](../tut/hda_flowsheet_solution_usr.ipynb) and the HDA with Distillation examples. The examples assume constant cooling and heating coefficients over an annual cost basis. The IDAES Generic Costing Framework does not currently support variable cost calculations."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Required imports\n",
- "from pyomo.environ import Expression\n",
- "\n",
- "# Operating costs for HDA with second flash (model m)\n",
- "m.fs.cooling_cost = Expression(\n",
- " expr=0.212e-7 * (-m.fs.F101.heat_duty[0]) + 0.212e-7 * (-m.fs.R101.heat_duty[0])\n",
- ")\n",
- "m.fs.heating_cost = Expression(\n",
- " expr=2.2e-7 * m.fs.H101.heat_duty[0] + 1.9e-7 * m.fs.F102.heat_duty[0]\n",
- ")\n",
- "m.fs.operating_cost = Expression(\n",
- " expr=(3600 * 24 * 365 * (m.fs.heating_cost + m.fs.cooling_cost))\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Add Capital Costing\n",
- "Below, we will add add capital costing blocks to the imported flowsheets and evaluate the economic impact of replacing the second Flash with a Distillation column. First, let's import and define the main flowsheet costing block:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Import costing methods - classes, heaters, vessels, compressors, columns\n",
- "from idaes.models.costing.SSLW import (\n",
- " SSLWCosting,\n",
- " SSLWCostingData,\n",
- ")\n",
- "from idaes.core import UnitModelCostingBlock\n",
- "\n",
- "# Costing block\n",
- "m.fs.costing = SSLWCosting()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Next, we will build the relevant costing blocks for the equipment we wish to cost. Note how the costing block, methods and flags are passed as arguments in the costing block call itself. Each unit model will have a single costing block, but each flowsheet model (m and n) will also have a single costing block for flowsheet-level properties.\n",
- "\n",
- "Users should note that IDAES costing methods support a wide array of heating sources (e.g. fired, steam boiler, hot water) and do not support direct capital costing of coolers. If users wish to cost Heater units acting as coolers, it is necessary to cost a \"dummy\" [0D shell and tube exchanger](https://idaes-pse.readthedocs.io/en/stable/reference_guides/model_libraries/generic/unit_models/heat_exchanger.html) with appropriate aliased hot stream properties and proper cooling water properties. This is not demonstrated here, as the HDA examples take advantage of Flash and Condenser operations to recover liquid product.\n",
- "\n",
- "Capital costing is independent of unit model connections, and building cost equations may be done piecewise in this fashion. Default options are passed explicitly to demonstrate proper syntax and usage. Now that all required properties are defined, let's cost our models connecting costing blocks, methods and unit models in each flowsheet."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Flexibility of Costing Block Definitions\n",
- "IDAES supports many ways to define batches of costing blocks, and several are shown in the example. Users may employ whichever method fits their modeling needs for explicit or concise code. In the code below, note how the unit model itself is never passed to the costing method; when the full model is executed, the costing block will automatically connect its parent block with child equation blocks.\n",
- "\n",
- "`Compressor` unit models with isothermal or adiabatic thermodynamics are too simple to cost and are therefore excluded from the economic analysis."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Let's define costing for the heater unit:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {},
- "outputs": [],
- "source": [
- "from idaes.models.costing.SSLW import (\n",
- " HeaterMaterial,\n",
- " HeaterSource,\n",
- ")\n",
- "\n",
- "# Costing for heater - m.fs.H101\n",
- "m.fs.H101.costing = UnitModelCostingBlock(\n",
- " flowsheet_costing_block=m.fs.costing,\n",
- " costing_method=SSLWCostingData.cost_fired_heater,\n",
- " costing_method_arguments={\n",
- " \"material_type\": HeaterMaterial.CarbonSteel,\n",
- " \"heat_source\": HeaterSource.Fuel,\n",
- " },\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The costing module provides a `unit_mapping` dictionary linking generic unit model classes with recommended costing methods. In this example, StoichiometricReactor and Flash vessels utilize different vessel costing methods with similar arguments. The diameter and length attributes need to exist in order to cost vessel sizing and material requirements, and we add them if they don't exist already. The `unit_mapping` method provides an opportunity to automatically select the correct vessel orientation (vertical or horizontal) based on the unit type; passing a `StoichiometricReactor` or `PFR` class object will call the `cost_horizontal_vessel` method, while passing a `Flash` or `CSTR` class object will call the `cost_vertical_vessel` method.\n",
- "\n",
- "All vessels are assigned costing succintly via a loop below - users may define each block individually if desired:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [],
- "source": [
- "from idaes.models.costing.SSLW import (\n",
- " VesselMaterial,\n",
- " TrayType,\n",
- " TrayMaterial,\n",
- ")\n",
- "\n",
- "from idaes.core.util.constants import Constants\n",
- "from pyomo.environ import Var, Constraint, units as pyunits, Param, value\n",
- "from idaes.models.unit_models import StoichiometricReactor, Flash\n",
- "\n",
- "# Map unit models to unit classes\n",
- "# Will pass to unit_mapping which calls costing methods based on unit class\n",
- "unit_class_mapping = {\n",
- " m.fs.R101: StoichiometricReactor,\n",
- " m.fs.F101: Flash,\n",
- " m.fs.F102: Flash,\n",
- "}\n",
- "\n",
- "# Costing for vessels - m.fs.R101, m.fs.F101, m.fs.F102\n",
- "\n",
- "# Loop over units\n",
- "for unit in [m.fs.R101, m.fs.F101, m.fs.F102]:\n",
- " # Get correct unit class for unit model\n",
- " unit_class = unit_class_mapping[unit]\n",
- "\n",
- " # Add dimension variables and constraint if they don't exist\n",
- " if not hasattr(unit, \"diameter\"):\n",
- " unit.diameter = Var(initialize=1, units=pyunits.m)\n",
- " if not hasattr(unit, \"length\"):\n",
- " unit.length = Var(initialize=1, units=pyunits.m)\n",
- " if hasattr(unit, \"volume\"): # if volume exists, set diameter from volume\n",
- " unit.volume_eq = Constraint(\n",
- " expr=unit.volume[0]\n",
- " == unit.length * unit.diameter**2 * 0.25 * Constants.pi\n",
- " )\n",
- " else: # fix diameter directly\n",
- " unit.diameter.fix(0.2214 * pyunits.m)\n",
- " # Either way, fix L/D to calculate L from D\n",
- " unit.L_over_D = Constraint(expr=unit.length == 3 * unit.diameter)\n",
- "\n",
- " # Define vessel costing\n",
- " unit.costing = UnitModelCostingBlock(\n",
- " flowsheet_costing_block=unit.parent_block().costing, # e.g. m.fs.R101.costing\n",
- " costing_method=SSLWCostingData.unit_mapping[\n",
- " unit_class\n",
- " ], # e.g. cost_vertical_vessel()\n",
- " costing_method_arguments={\n",
- " \"material_type\": VesselMaterial.CarbonSteel,\n",
- " \"shell_thickness\": 1.25 * pyunits.inch,\n",
- " },\n",
- " )"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Solve Flowsheet Costing Blocks\n",
- "Now, we may solve the full flowsheet for all costing blocks:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Eefine solver\n",
- "from idaes.core.solvers import get_solver\n",
- "\n",
- "solver = get_solver()\n",
- "\n",
- "# Check that the degrees of freedom is zero\n",
- "from idaes.core.util.model_statistics import degrees_of_freedom\n",
- "\n",
- "assert degrees_of_freedom(m) == 0"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Check physical units consistency, solve and check solver status\n",
- "from pyomo.environ import TerminationCondition\n",
- "from pyomo.util.check_units import assert_units_consistent\n",
- "\n",
- "assert_units_consistent(m)\n",
- "results = solver.solve(m, tee=True, symbolic_solver_labels=True)\n",
- "assert results.solver.termination_condition == TerminationCondition.optimal"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "For comparison, we will call and build the HDA flowsheet replacing the second `Flash` with a `TrayColumn` distillation unit model. The flowsheet costing occurs in the external script `hda_flowsheets_for_costing_notebook.py` and is not shown here:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "metadata": {
- "scrolled": true
- },
- "outputs": [],
- "source": [
- "from pyomo.common.log import LoggingIntercept\n",
- "import logging\n",
- "from io import StringIO\n",
- "\n",
- "stream = StringIO()\n",
- "with LoggingIntercept(stream, \"idaes\", logging.WARNING):\n",
- " # Source file for prebuilt flowsheets\n",
- " from hda_flowsheets_for_costing_notebook import hda_with_distillation\n",
- "\n",
- " # Build hda model with distillation column and return model object\n",
- " n = hda_with_distillation(tee=False)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Results Comparison and Visualization\n",
- "For the two flowsheets above, let's sum the total operating and capital costs of each scenario. We will display overall process economics results and compare the two flowsheets:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Imports and data gathering\n",
- "from matplotlib import pyplot as plt\n",
- "\n",
- "plt.style.use(\"dark_background\") # if using browser in dark mode, uncomment this line\n",
- "import numpy as np\n",
- "import pandas as pd\n",
- "\n",
- "# Automatically get units that we costed - this will exclude C101 for both flowsheets\n",
- "\n",
- "two_flash_unitlist = [\n",
- " getattr(m.fs, unit) for unit in dir(m.fs) if hasattr(getattr(m.fs, unit), \"costing\")\n",
- "]\n",
- "distillation_unitlist = [\n",
- " getattr(n.fs, unit) for unit in dir(n.fs) if hasattr(getattr(n.fs, unit), \"costing\")\n",
- "]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Compare equipment purchase costs (actual capital costs)\n",
- "\n",
- "two_flash_capcost = {\n",
- " unit.name: value(unit.costing.capital_cost / 1e3) for unit in two_flash_unitlist\n",
- "}\n",
- "distillation_capcost = {\n",
- " unit.name: value(unit.costing.capital_cost / 1e3) for unit in distillation_unitlist\n",
- "}\n",
- "\n",
- "two_flash_capdf = pd.DataFrame(\n",
- " list(two_flash_capcost.items()), columns=[\"Equipment\", \"Two Flash\"]\n",
- ").set_index(\"Equipment\")\n",
- "distillation_capdf = pd.DataFrame(\n",
- " list(distillation_capcost.items()), columns=[\"Equipment\", \"Distillation\"]\n",
- ").set_index(\"Equipment\")\n",
- "\n",
- "# Add dataframes, merge same indices, replace NaNs with 0s, and transpose\n",
- "capcosts = two_flash_capdf.add(distillation_capdf, fill_value=0).fillna(0).transpose()\n",
- "\n",
- "# Sort according to an easier order to view\n",
- "capcosts = capcosts[[\"fs.H101\", \"fs.R101\", \"fs.F101\", \"fs.F102\", \"fs.D101\", \"fs.H102\"]]\n",
- "\n",
- "print(\"Costs in $1000:\")\n",
- "display(capcosts) # view dataframe before plotting\n",
- "\n",
- "capplot = capcosts.plot(\n",
- " kind=\"bar\", stacked=True, title=\"HDA Total Capital Costs\", ylabel=\"$1000\"\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 11,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Compare operating costs (per year)\n",
- "\n",
- "two_flash_opcost = {\n",
- " \"cooling\": value(3600 * 24 * 365 * m.fs.cooling_cost / 1e3),\n",
- " \"heating\": value(3600 * 24 * 365 * m.fs.heating_cost / 1e3),\n",
- "}\n",
- "distillation_opcost = {\n",
- " \"cooling\": value(3600 * 24 * 365 * n.fs.cooling_cost / 1e3),\n",
- " \"heating\": value(3600 * 24 * 365 * n.fs.heating_cost / 1e3),\n",
- "}\n",
- "\n",
- "two_flash_opdf = pd.DataFrame(\n",
- " list(two_flash_opcost.items()), columns=[\"Utilities\", \"Two Flash\"]\n",
- ").set_index(\"Utilities\")\n",
- "distillation_opdf = pd.DataFrame(\n",
- " list(distillation_opcost.items()), columns=[\"Utilities\", \"Distillation\"]\n",
- ").set_index(\"Utilities\")\n",
- "\n",
- "# Add dataframes, merge same indices, replace NaNs with 0s, and transpose\n",
- "opcosts = two_flash_opdf.add(distillation_opdf, fill_value=0).fillna(0).transpose()\n",
- "\n",
- "print(\"Costs in $1000:\")\n",
- "display(opcosts) # view dataframe before plotting\n",
- "\n",
- "opplot = opcosts.plot(\n",
- " kind=\"bar\", stacked=True, title=\"HDA Operating Costs\", ylabel=\"$1000/year\"\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 12,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Compare total costs (capital costs and operating costs)\n",
- "\n",
- "two_flash_totcost = {\n",
- " \"capital\": sum(two_flash_capcost[idx] for idx in two_flash_capcost),\n",
- " \"operating\": value(m.fs.operating_cost) / 1e3,\n",
- "}\n",
- "distillation_totcost = {\n",
- " \"capital\": sum(distillation_capcost[idx] for idx in distillation_capcost),\n",
- " \"operating\": value(n.fs.operating_cost) / 1e3,\n",
- "}\n",
- "\n",
- "two_flash_totdf = pd.DataFrame(\n",
- " list(two_flash_totcost.items()), columns=[\"Costs\", \"Two Flash\"]\n",
- ").set_index(\"Costs\")\n",
- "distillation_totdf = pd.DataFrame(\n",
- " list(distillation_totcost.items()), columns=[\"Costs\", \"Distillation\"]\n",
- ").set_index(\"Costs\")\n",
- "\n",
- "# Add dataframes, merge same indices, replace NaNs with 0s, and transpose\n",
- "totcosts = two_flash_totdf.add(distillation_totdf, fill_value=0).fillna(0).transpose()\n",
- "\n",
- "print(\"Costs in $1000:\")\n",
- "display(totcosts) # view dataframe before plotting\n",
- "\n",
- "totplot = totcosts.plot(\n",
- " kind=\"bar\", stacked=True, title=\"HDA Total Plant Cost (TPC)\", ylabel=\"$1000/year\"\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Finally, let's compare the total costs on a production basis. This will account for the greater efficiency provided by the distillation column relative to the less-expensive second flash unit:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 13,
- "metadata": {},
- "outputs": [],
- "source": [
- "two_flash_cost = value(1e3 * sum(two_flash_totcost[idx] for idx in two_flash_totcost))\n",
- "two_flash_prod = value(\n",
- " m.fs.F102.vap_outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"] * 365 * 24 * 3600\n",
- ")\n",
- "distillation_cost = value(\n",
- " 1e3 * sum(distillation_totcost[idx] for idx in distillation_totcost)\n",
- ")\n",
- "distillation_prod = value(n.fs.D101.condenser.distillate.flow_mol[0] * 365 * 24 * 3600)\n",
- "\n",
- "print(\n",
- " f\"Two flash case over one year: ${two_flash_cost/1e3:0.0f}K / {two_flash_prod/1e3:0.0f} kmol benzene = ${two_flash_cost/(two_flash_prod/1e3):0.2f} per kmol benzene produced\"\n",
- ")\n",
- "print(\n",
- " f\"Distillation case over one year: ${distillation_cost/1e3:0.0f}K / {distillation_prod/1e3:0.0f} kmol benzene = ${distillation_cost/(distillation_prod/1e3):0.2f} per kmol benzene produced\"\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Summary\n",
- "In this example, IDAES Process Costing Framework methods were applied to two HDA flowsheets for capital cost estimation. The costing blocks calls showcased multiple methods to define unit costing, demonstrating the flexibility and best practice of the costing framework. In the basic examples, the two-flash HDA did not include costing and the distillation HDA estimated a reactor capital cost comprising 3.3% of the total plant cost (TPC). With more rigorous costing, IDAES obtained total capital costs of 8.5% TPC (two flash HDA) and 9.6% (distillation HDA) and better modeled the impact of equipment cost on process economics. As printed above, the IDAES Process Costing Framework confirmed that replacing the second flash drum with a distillation column results in increased equipment costs, increased production and decreased cost per unit product."
- ]
- }
- ],
- "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.8.12"
- }
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "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",
+ "###############################################################################"
+ ]
},
- "nbformat": 4,
- "nbformat_minor": 3
-}
\ No newline at end of file
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "# HDA Flowsheet Costing\n",
+ "Maintainer: Brandon Paul \n",
+ "Author: Brandon Paul \n",
+ "Updated: 2023-06-01 \n",
+ "\n",
+ "\n",
+ "## Note\n",
+ "\n",
+ "This example will demonstrate adding capital and operating costs to the two HDA examples, the basic [HDA with Flash](../tut/hda_flowsheet_solution_usr.ipynb) and a comparison with the HDA with Distillation.\n",
+ "\n",
+ "\n",
+ "## Learning outcomes\n",
+ "\n",
+ "- Import external pre-built steady-state flowsheets using the IDAES unit model library\n",
+ "- Define and add costing blocks using the IDAES Process Costing Framework\n",
+ "- Fomulate and solve a process economics optimization problem\n",
+ " - Defining an objective function\n",
+ " - Setting variable bounds\n",
+ " - Adding additional constraints \n",
+ "\n",
+ "\n",
+ "## Problem Statement\n",
+ "\n",
+ "Hydrodealkylation is a chemical reaction that often involves reacting\n",
+ "an aromatic hydrocarbon in the presence of hydrogen gas to form a\n",
+ "simpler aromatic hydrocarbon devoid of functional groups. In this\n",
+ "example, toluene will be reacted with hydrogen gas at high temperatures\n",
+ " to form benzene via the following reaction:\n",
+ "\n",
+ "**C6H5CH3 + H2 → C6H6 + CH4**\n",
+ "\n",
+ "\n",
+ "This reaction is often accompanied by an equilibrium side reaction\n",
+ "which forms diphenyl, which we will neglect for this example.\n",
+ "\n",
+ "This example is based on the 1967 AIChE Student Contest problem as\n",
+ "present by Douglas, J.M., Chemical Design of Chemical Processes, 1988,\n",
+ "McGraw-Hill.\n",
+ "\n",
+ "Users may refer to the prior examples linked at the top of this notebook for detailed process descriptions of the two HDA configurations. As before, the properties required for this module are defined in\n",
+ "\n",
+ "- `hda_ideal_VLE.py`\n",
+ "- `idaes.models.properties.activity_coeff_models.BTX_activity_coeff_VLE`\n",
+ "- `hda_reaction.py`\n",
+ "\n",
+ "Additionally, we will be importing externally-defined flowsheets for the two HDA configurations from\n",
+ "\n",
+ "- `hda_flowsheets_for_costing_notebook.py`"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Import and run HDA Flowsheets\n",
+ "First, we will generate solved flowsheets for each HDA model. The external scripts build and set inputs for the flowsheets, initialize unit models and streams, and solve the flowsheets before returning the model objects. Note that the HDA flowsheets contain all unit models and stream connections, and no costing equations."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The flowsheet utilizes the Wegstein method to iteratively solve circular dependencies such as recycle streams, and is intended to approach a feasible solution. As such, the calls below will fail to converge after 3 iterations and pass to IPOPT to obtain an optimal solution as expected:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {
+ "scrolled": true
+ },
+ "outputs": [],
+ "source": [
+ "# Source file for prebuilt flowsheets\n",
+ "from hda_flowsheets_for_costing_notebook import hda_with_flash\n",
+ "\n",
+ "# Build hda model with second flash unit and return model object\n",
+ "m = hda_with_flash(tee=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## IDAES Process Costing Framework\n",
+ "IDAES provides a library of capital costing correlations based on those in the following source:\n",
+ "\n",
+ "*Process and Product Design Principles: Synthesis, Analysis, and Evaluation*. Seider, Seader, Lewin, Windagdo, 3rd Ed. John Wiley and Sons Chapter 22. Cost Accounting and Capital Cost Estimation 22.2 Cost Indexes and Capital Investment.\n",
+ "\n",
+ "Currently, IDAES supports calculation of capital costing for a wide array of unit operations, vesseel sizing and material properties, and specific unit options such as column tray types and heat exchanger configurations. Users may find further information on specific costing methods and options in the IDAES Process Costing Framework documentation (link pending).\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Add Operating Cost Equations\n",
+ "Before adding capital costing blocks, we will add operating cost equations taken from the basic [HDA with Flash](../tut/hda_flowsheet_solution_usr.ipynb) and the HDA with Distillation examples. The examples assume constant cooling and heating coefficients over an annual cost basis. The IDAES Generic Costing Framework does not currently support variable cost calculations."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Required imports\n",
+ "from pyomo.environ import Expression\n",
+ "\n",
+ "# Operating costs for HDA with second flash (model m)\n",
+ "m.fs.cooling_cost = Expression(\n",
+ " expr=0.212e-7 * (-m.fs.F101.heat_duty[0]) + 0.212e-7 * (-m.fs.R101.heat_duty[0])\n",
+ ")\n",
+ "m.fs.heating_cost = Expression(\n",
+ " expr=2.2e-7 * m.fs.H101.heat_duty[0] + 1.9e-7 * m.fs.F102.heat_duty[0]\n",
+ ")\n",
+ "m.fs.operating_cost = Expression(\n",
+ " expr=(3600 * 24 * 365 * (m.fs.heating_cost + m.fs.cooling_cost))\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Add Capital Costing\n",
+ "Below, we will add add capital costing blocks to the imported flowsheets and evaluate the economic impact of replacing the second Flash with a Distillation column. First, let's import and define the main flowsheet costing block:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Import costing methods - classes, heaters, vessels, compressors, columns\n",
+ "from idaes.models.costing.SSLW import (\n",
+ " SSLWCosting,\n",
+ " SSLWCostingData,\n",
+ ")\n",
+ "from idaes.core import UnitModelCostingBlock\n",
+ "\n",
+ "# Costing block\n",
+ "m.fs.costing = SSLWCosting()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Next, we will build the relevant costing blocks for the equipment we wish to cost. Note how the costing block, methods and flags are passed as arguments in the costing block call itself. Each unit model will have a single costing block, but each flowsheet model (m and n) will also have a single costing block for flowsheet-level properties.\n",
+ "\n",
+ "Users should note that IDAES costing methods support a wide array of heating sources (e.g. fired, steam boiler, hot water) and do not support direct capital costing of coolers. If users wish to cost Heater units acting as coolers, it is necessary to cost a \"dummy\" [0D shell and tube exchanger](https://idaes-pse.readthedocs.io/en/stable/reference_guides/model_libraries/generic/unit_models/heat_exchanger.html) with appropriate aliased hot stream properties and proper cooling water properties. This is not demonstrated here, as the HDA examples take advantage of Flash and Condenser operations to recover liquid product.\n",
+ "\n",
+ "Capital costing is independent of unit model connections, and building cost equations may be done piecewise in this fashion. Default options are passed explicitly to demonstrate proper syntax and usage. Now that all required properties are defined, let's cost our models connecting costing blocks, methods and unit models in each flowsheet."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Flexibility of Costing Block Definitions\n",
+ "IDAES supports many ways to define batches of costing blocks, and several are shown in the example. Users may employ whichever method fits their modeling needs for explicit or concise code. In the code below, note how the unit model itself is never passed to the costing method; when the full model is executed, the costing block will automatically connect its parent block with child equation blocks.\n",
+ "\n",
+ "`Compressor` unit models with isothermal or adiabatic thermodynamics are too simple to cost and are therefore excluded from the economic analysis."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let's define costing for the heater unit:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from idaes.models.costing.SSLW import (\n",
+ " HeaterMaterial,\n",
+ " HeaterSource,\n",
+ ")\n",
+ "\n",
+ "# Costing for heater - m.fs.H101\n",
+ "m.fs.H101.costing = UnitModelCostingBlock(\n",
+ " flowsheet_costing_block=m.fs.costing,\n",
+ " costing_method=SSLWCostingData.cost_fired_heater,\n",
+ " costing_method_arguments={\n",
+ " \"material_type\": HeaterMaterial.CarbonSteel,\n",
+ " \"heat_source\": HeaterSource.Fuel,\n",
+ " },\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The costing module provides a `unit_mapping` dictionary linking generic unit model classes with recommended costing methods. In this example, StoichiometricReactor and Flash vessels utilize different vessel costing methods with similar arguments. The diameter and length attributes need to exist in order to cost vessel sizing and material requirements, and we add them if they don't exist already. The `unit_mapping` method provides an opportunity to automatically select the correct vessel orientation (vertical or horizontal) based on the unit type; passing a `StoichiometricReactor` or `PFR` class object will call the `cost_horizontal_vessel` method, while passing a `Flash` or `CSTR` class object will call the `cost_vertical_vessel` method.\n",
+ "\n",
+ "All vessels are assigned costing succinctly via a loop below - users may define each block individually if desired:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from idaes.models.costing.SSLW import (\n",
+ " VesselMaterial,\n",
+ " TrayType,\n",
+ " TrayMaterial,\n",
+ ")\n",
+ "\n",
+ "from idaes.core.util.constants import Constants\n",
+ "from pyomo.environ import Var, Constraint, units as pyunits, Param, value\n",
+ "from idaes.models.unit_models import StoichiometricReactor, Flash\n",
+ "\n",
+ "# Map unit models to unit classes\n",
+ "# Will pass to unit_mapping which calls costing methods based on unit class\n",
+ "unit_class_mapping = {\n",
+ " m.fs.R101: StoichiometricReactor,\n",
+ " m.fs.F101: Flash,\n",
+ " m.fs.F102: Flash,\n",
+ "}\n",
+ "\n",
+ "# Costing for vessels - m.fs.R101, m.fs.F101, m.fs.F102\n",
+ "\n",
+ "# Loop over units\n",
+ "for unit in [m.fs.R101, m.fs.F101, m.fs.F102]:\n",
+ " # Get correct unit class for unit model\n",
+ " unit_class = unit_class_mapping[unit]\n",
+ "\n",
+ " # Add dimension variables and constraint if they don't exist\n",
+ " if not hasattr(unit, \"diameter\"):\n",
+ " unit.diameter = Var(initialize=1, units=pyunits.m)\n",
+ " if not hasattr(unit, \"length\"):\n",
+ " unit.length = Var(initialize=1, units=pyunits.m)\n",
+ " if hasattr(unit, \"volume\"): # if volume exists, set diameter from volume\n",
+ " unit.volume_eq = Constraint(\n",
+ " expr=unit.volume[0] == unit.length * unit.diameter**2 * 0.25 * Constants.pi\n",
+ " )\n",
+ " else: # fix diameter directly\n",
+ " unit.diameter.fix(0.2214 * pyunits.m)\n",
+ " # Either way, fix L/D to calculate L from D\n",
+ " unit.L_over_D = Constraint(expr=unit.length == 3 * unit.diameter)\n",
+ "\n",
+ " # Define vessel costing\n",
+ " unit.costing = UnitModelCostingBlock(\n",
+ " flowsheet_costing_block=unit.parent_block().costing, # e.g. m.fs.R101.costing\n",
+ " costing_method=SSLWCostingData.unit_mapping[\n",
+ " unit_class\n",
+ " ], # e.g. cost_vertical_vessel()\n",
+ " costing_method_arguments={\n",
+ " \"material_type\": VesselMaterial.CarbonSteel,\n",
+ " \"shell_thickness\": 1.25 * pyunits.inch,\n",
+ " },\n",
+ " )"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Solve Flowsheet Costing Blocks\n",
+ "Now, we may solve the full flowsheet for all costing blocks:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Eefine solver\n",
+ "from idaes.core.solvers import get_solver\n",
+ "\n",
+ "solver = get_solver()\n",
+ "\n",
+ "# Check that the degrees of freedom is zero\n",
+ "from idaes.core.util.model_statistics import degrees_of_freedom\n",
+ "\n",
+ "assert degrees_of_freedom(m) == 0"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Check physical units consistency, solve and check solver status\n",
+ "from pyomo.environ import TerminationCondition\n",
+ "from pyomo.util.check_units import assert_units_consistent\n",
+ "\n",
+ "assert_units_consistent(m)\n",
+ "results = solver.solve(m, tee=True, symbolic_solver_labels=True)\n",
+ "assert results.solver.termination_condition == TerminationCondition.optimal"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "For comparison, we will call and build the HDA flowsheet replacing the second `Flash` with a `TrayColumn` distillation unit model. The flowsheet costing occurs in the external script `hda_flowsheets_for_costing_notebook.py` and is not shown here:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {
+ "scrolled": true
+ },
+ "outputs": [],
+ "source": [
+ "from pyomo.common.log import LoggingIntercept\n",
+ "import logging\n",
+ "from io import StringIO\n",
+ "\n",
+ "stream = StringIO()\n",
+ "with LoggingIntercept(stream, \"idaes\", logging.WARNING):\n",
+ " # Source file for prebuilt flowsheets\n",
+ " from hda_flowsheets_for_costing_notebook import hda_with_distillation\n",
+ "\n",
+ " # Build hda model with distillation column and return model object\n",
+ " n = hda_with_distillation(tee=False)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Results Comparison and Visualization\n",
+ "For the two flowsheets above, let's sum the total operating and capital costs of each scenario. We will display overall process economics results and compare the two flowsheets:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Imports and data gathering\n",
+ "from matplotlib import pyplot as plt\n",
+ "\n",
+ "plt.style.use(\"dark_background\") # if using browser in dark mode, uncomment this line\n",
+ "import numpy as np\n",
+ "import pandas as pd\n",
+ "\n",
+ "# Automatically get units that we costed - this will exclude C101 for both flowsheets\n",
+ "\n",
+ "two_flash_unitlist = [\n",
+ " getattr(m.fs, unit) for unit in dir(m.fs) if hasattr(getattr(m.fs, unit), \"costing\")\n",
+ "]\n",
+ "distillation_unitlist = [\n",
+ " getattr(n.fs, unit) for unit in dir(n.fs) if hasattr(getattr(n.fs, unit), \"costing\")\n",
+ "]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Compare equipment purchase costs (actual capital costs)\n",
+ "\n",
+ "two_flash_capcost = {\n",
+ " unit.name: value(unit.costing.capital_cost / 1e3) for unit in two_flash_unitlist\n",
+ "}\n",
+ "distillation_capcost = {\n",
+ " unit.name: value(unit.costing.capital_cost / 1e3) for unit in distillation_unitlist\n",
+ "}\n",
+ "\n",
+ "two_flash_capdf = pd.DataFrame(\n",
+ " list(two_flash_capcost.items()), columns=[\"Equipment\", \"Two Flash\"]\n",
+ ").set_index(\"Equipment\")\n",
+ "distillation_capdf = pd.DataFrame(\n",
+ " list(distillation_capcost.items()), columns=[\"Equipment\", \"Distillation\"]\n",
+ ").set_index(\"Equipment\")\n",
+ "\n",
+ "# Add dataframes, merge same indices, replace NaNs with 0s, and transpose\n",
+ "capcosts = two_flash_capdf.add(distillation_capdf, fill_value=0).fillna(0).transpose()\n",
+ "\n",
+ "# Sort according to an easier order to view\n",
+ "capcosts = capcosts[[\"fs.H101\", \"fs.R101\", \"fs.F101\", \"fs.F102\", \"fs.D101\", \"fs.H102\"]]\n",
+ "\n",
+ "print(\"Costs in $1000:\")\n",
+ "display(capcosts) # view dataframe before plotting\n",
+ "\n",
+ "capplot = capcosts.plot(\n",
+ " kind=\"bar\", stacked=True, title=\"HDA Total Capital Costs\", ylabel=\"$1000\"\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Compare operating costs (per year)\n",
+ "\n",
+ "two_flash_opcost = {\n",
+ " \"cooling\": value(3600 * 24 * 365 * m.fs.cooling_cost / 1e3),\n",
+ " \"heating\": value(3600 * 24 * 365 * m.fs.heating_cost / 1e3),\n",
+ "}\n",
+ "distillation_opcost = {\n",
+ " \"cooling\": value(3600 * 24 * 365 * n.fs.cooling_cost / 1e3),\n",
+ " \"heating\": value(3600 * 24 * 365 * n.fs.heating_cost / 1e3),\n",
+ "}\n",
+ "\n",
+ "two_flash_opdf = pd.DataFrame(\n",
+ " list(two_flash_opcost.items()), columns=[\"Utilities\", \"Two Flash\"]\n",
+ ").set_index(\"Utilities\")\n",
+ "distillation_opdf = pd.DataFrame(\n",
+ " list(distillation_opcost.items()), columns=[\"Utilities\", \"Distillation\"]\n",
+ ").set_index(\"Utilities\")\n",
+ "\n",
+ "# Add dataframes, merge same indices, replace NaNs with 0s, and transpose\n",
+ "opcosts = two_flash_opdf.add(distillation_opdf, fill_value=0).fillna(0).transpose()\n",
+ "\n",
+ "print(\"Costs in $1000:\")\n",
+ "display(opcosts) # view dataframe before plotting\n",
+ "\n",
+ "opplot = opcosts.plot(\n",
+ " kind=\"bar\", stacked=True, title=\"HDA Operating Costs\", ylabel=\"$1000/year\"\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Compare total costs (capital costs and operating costs)\n",
+ "\n",
+ "two_flash_totcost = {\n",
+ " \"capital\": sum(two_flash_capcost[idx] for idx in two_flash_capcost),\n",
+ " \"operating\": value(m.fs.operating_cost) / 1e3,\n",
+ "}\n",
+ "distillation_totcost = {\n",
+ " \"capital\": sum(distillation_capcost[idx] for idx in distillation_capcost),\n",
+ " \"operating\": value(n.fs.operating_cost) / 1e3,\n",
+ "}\n",
+ "\n",
+ "two_flash_totdf = pd.DataFrame(\n",
+ " list(two_flash_totcost.items()), columns=[\"Costs\", \"Two Flash\"]\n",
+ ").set_index(\"Costs\")\n",
+ "distillation_totdf = pd.DataFrame(\n",
+ " list(distillation_totcost.items()), columns=[\"Costs\", \"Distillation\"]\n",
+ ").set_index(\"Costs\")\n",
+ "\n",
+ "# Add dataframes, merge same indices, replace NaNs with 0s, and transpose\n",
+ "totcosts = two_flash_totdf.add(distillation_totdf, fill_value=0).fillna(0).transpose()\n",
+ "\n",
+ "print(\"Costs in $1000:\")\n",
+ "display(totcosts) # view dataframe before plotting\n",
+ "\n",
+ "totplot = totcosts.plot(\n",
+ " kind=\"bar\", stacked=True, title=\"HDA Total Plant Cost (TPC)\", ylabel=\"$1000/year\"\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Finally, let's compare the total costs on a production basis. This will account for the greater efficiency provided by the distillation column relative to the less-expensive second flash unit:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "two_flash_cost = value(1e3 * sum(two_flash_totcost[idx] for idx in two_flash_totcost))\n",
+ "two_flash_prod = value(\n",
+ " m.fs.F102.vap_outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"] * 365 * 24 * 3600\n",
+ ")\n",
+ "distillation_cost = value(\n",
+ " 1e3 * sum(distillation_totcost[idx] for idx in distillation_totcost)\n",
+ ")\n",
+ "distillation_prod = value(n.fs.D101.condenser.distillate.flow_mol[0] * 365 * 24 * 3600)\n",
+ "\n",
+ "print(\n",
+ " f\"Two flash case over one year: ${two_flash_cost/1e3:0.0f}K / {two_flash_prod/1e3:0.0f} kmol benzene = ${two_flash_cost/(two_flash_prod/1e3):0.2f} per kmol benzene produced\"\n",
+ ")\n",
+ "print(\n",
+ " f\"Distillation case over one year: ${distillation_cost/1e3:0.0f}K / {distillation_prod/1e3:0.0f} kmol benzene = ${distillation_cost/(distillation_prod/1e3):0.2f} per kmol benzene produced\"\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Summary\n",
+ "In this example, IDAES Process Costing Framework methods were applied to two HDA flowsheets for capital cost estimation. The costing blocks calls showcased multiple methods to define unit costing, demonstrating the flexibility and best practice of the costing framework. In the basic examples, the two-flash HDA did not include costing and the distillation HDA estimated a reactor capital cost comprising 3.3% of the total plant cost (TPC). With more rigorous costing, IDAES obtained total capital costs of 8.5% TPC (two flash HDA) and 9.6% (distillation HDA) and better modeled the impact of equipment cost on process economics. As printed above, the IDAES Process Costing Framework confirmed that replacing the second flash drum with a distillation column results in increased equipment costs, increased production and decreased cost per unit product."
+ ]
+ }
+ ],
+ "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.8.12"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 3
+}
diff --git a/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_distillation.ipynb b/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_distillation.ipynb
index 0179081c..44e6cf27 100644
--- a/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_distillation.ipynb
+++ b/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_distillation.ipynb
@@ -80,8 +80,7 @@
"We will be using two thermodynamic packages: one (first in the list above) containing all four components (i.e., toluene, hydrogen, benzene, and methane) and the other (second in the list above) containing benzene and toluene only. The latter is needed to simplify the VLE calculations in the distillation column model. \n",
"\n",
"![](HDA_flowsheet_distillation.png)\n",
- "\n",
- ""
+ "\n"
]
},
{
@@ -235,6 +234,7 @@
"from idaes.core.util.initialization import propagate_state\n",
"from idaes.core.solvers import get_solver\n",
"import idaes.core.util.scaling as iscale\n",
+ "from idaes.core.util.exceptions import InitializationError\n",
"\n",
"# Import idaes logger to set output levels\n",
"import idaes.logger as idaeslog"
@@ -602,7 +602,7 @@
"source": [
"## Connecting Unit Models using Arcs\n",
"\n",
- "We have now added the initial set of unit models to the flowsheet. However, we have not yet specifed how the units are connected. To do this, we will be using the `Arc` which is a pyomo component that takes in two arguments: `source` and `destination`. Let us connect the outlet of the mixer (M101) to the inlet of the heater (H101). "
+ "We have now added the initial set of unit models to the flowsheet. However, we have not yet specified how the units are connected. To do this, we will be using the `Arc` which is a pyomo component that takes in two arguments: `source` and `destination`. Let us connect the outlet of the mixer (M101) to the inlet of the heater (H101). "
]
},
{
@@ -738,7 +738,15 @@
"cell_type": "code",
"execution_count": 28,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "29\n"
+ ]
+ }
+ ],
"source": [
"print(degrees_of_freedom(m))"
]
@@ -953,7 +961,42 @@
"cell_type": "code",
"execution_count": 38,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enth_mol_phase_comp[Liq,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enth_mol_phase_comp[Liq,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enth_mol_phase_comp[Vap,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enth_mol_phase_comp[Vap,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].enth_mol_phase_comp[Liq,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].enth_mol_phase_comp[Liq,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].enth_mol_phase_comp[Vap,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].enth_mol_phase_comp[Vap,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].material_flow_terms[Liq,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].material_flow_terms[Vap,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].material_flow_terms[Liq,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].material_flow_terms[Vap,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enthalpy_flow_terms[Liq], enthalpy_flow_terms\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enthalpy_flow_terms[Vap], enthalpy_flow_terms\n"
+ ]
+ }
+ ],
"source": [
"# Set scaling factors for heat duty, reaction extent and volume\n",
"iscale.set_scaling_factor(m.fs.H101.control_volume.heat, 1e-2)\n",
@@ -1003,7 +1046,15 @@
"solution"
]
},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "0\n"
+ ]
+ }
+ ],
"source": [
"# Todo: Check the degrees of freedom\n",
"print(degrees_of_freedom(m))"
@@ -1062,7 +1113,15 @@
"cell_type": "code",
"execution_count": 43,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "fs.s03\n"
+ ]
+ }
+ ],
"source": [
"for o in heuristic_tear_set:\n",
" print(o.name)"
@@ -1079,7 +1138,20 @@
"cell_type": "code",
"execution_count": 44,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "fs.H101\n",
+ "fs.R101\n",
+ "fs.F101\n",
+ "fs.S101\n",
+ "fs.C101\n",
+ "fs.M101\n"
+ ]
+ }
+ ],
"source": [
"for o in order:\n",
" print(o[0].name)"
@@ -1111,7 +1183,7 @@
" (0, \"Liq\", \"hydrogen\"): 1e-5,\n",
" (0, \"Liq\", \"methane\"): 1e-5,\n",
" },\n",
- " \"temperature\": {0: 303},\n",
+ " \"temperature\": {0: 303.2},\n",
" \"pressure\": {0: 350000},\n",
"}\n",
"\n",
@@ -1123,7 +1195,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "Next, we need to tell the tool how to initialize a particular unit. We will be writing a python function which takes in a \"unit\" and calls the initialize method on that unit."
+ "Next, we need to tell the tool how to initialize a particular unit. We will be writing a python function which takes in a \"unit\" and calls the initialize method on that unit. For the initialization, we will import a Block Triangularization Initializer which decomposes the model into a set of subproblems. These subproblems are solved using a block triangularization transformation before applying a simple Newton or user-selected solver. Methods such as block triangularization often solve faster and yield more reliable behavior than heuristic methods, but sometime struggle to decompose models with strongly coupled equations (e.g. column models, systems with counter-current flow, vapor-liquid equilibrium)."
]
},
{
@@ -1133,7 +1205,14 @@
"outputs": [],
"source": [
"def function(unit):\n",
- " unit.initialize(outlvl=idaeslog.INFO)"
+ " # Try initializing using default initializer,\n",
+ " # if it fails (probably due to scaling) try for the second time\n",
+ " try:\n",
+ " initializer = unit.default_initializer()\n",
+ " initializer.initialize(unit, output_level=idaeslog.INFO)\n",
+ " except InitializationError:\n",
+ " solver = get_solver()\n",
+ " solver.solve(unit)"
]
},
{
@@ -1149,7 +1228,132 @@
"metadata": {
"scrolled": false
},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2024-08-28 18:38:14 [INFO] idaes.init.fs.H101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:16 [INFO] idaes.init.fs.H101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:16 [INFO] idaes.init.fs.R101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:16 [INFO] idaes.init.fs.R101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.F101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.F101.control_volume.properties_out: Initialization Complete\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 12\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 50 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.S101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - \n",
+ "2024-08-28 18:38:18 [INFO] idaes.init.fs.C101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:18 [INFO] idaes.init.fs.C101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:18 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 1 optimal - .\n",
+ "2024-08-28 18:38:18 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 2 optimal - .\n",
+ "2024-08-28 18:38:19 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 3 optimal - .\n",
+ "2024-08-28 18:38:19 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 4 optimal - .\n",
+ "2024-08-28 18:38:19 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 5 optimal - .\n",
+ "2024-08-28 18:38:19 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 1 optimal - .\n",
+ "2024-08-28 18:38:19 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 2 optimal - .\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 3 optimal - .\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 4 optimal - .\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 5 optimal - .\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.M101.toluene_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.M101.hydrogen_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.M101.vapor_recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:21 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:21 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - \n",
+ "2024-08-28 18:38:21 [INFO] idaes.init.fs.H101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:21 [INFO] idaes.init.fs.H101.control_volume.properties_out: Initialization Complete\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 11\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 50 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.R101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.R101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.F101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.F101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.S101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - \n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.C101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.C101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.M101.toluene_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.M101.hydrogen_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.M101.vapor_recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - \n",
+ "2024-08-28 18:38:24 [INFO] idaes.init.fs.H101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:24 [INFO] idaes.init.fs.H101.control_volume.properties_out: Initialization Complete\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 11\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 50 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "2024-08-28 18:38:24 [INFO] idaes.init.fs.R101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:24 [INFO] idaes.init.fs.R101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.F101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.F101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.S101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - \n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.C101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.C101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.M101.toluene_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.M101.hydrogen_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.M101.vapor_recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - \n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.H101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.H101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.R101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.R101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.F101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.F101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.S101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - \n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.C101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.C101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.M101.toluene_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.M101.hydrogen_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.M101.vapor_recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - \n",
+ "2024-08-28 18:38:29 [INFO] idaes.init.fs.H101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:29 [INFO] idaes.init.fs.H101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:29 [INFO] idaes.init.fs.R101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:29 [INFO] idaes.init.fs.R101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.F101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.F101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.S101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - \n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.C101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.C101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.M101.toluene_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.M101.hydrogen_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:31 [INFO] idaes.init.fs.M101.vapor_recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:31 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:31 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - \n",
+ "WARNING: Wegstein failed to converge in 3 iterations\n",
+ "2024-08-28 18:38:31 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 1 optimal - .\n",
+ "2024-08-28 18:38:31 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 2 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 3 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 4 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 5 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 1 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 2 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 3 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 4 optimal - .\n",
+ "2024-08-28 18:38:33 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 5 optimal - .\n"
+ ]
+ }
+ ],
"source": [
"seq.run(m, function)"
]
@@ -1168,7 +1372,91 @@
"cell_type": "code",
"execution_count": 48,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "WARNING: model contains export suffix 'scaling_factor' that contains 6\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 150 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "Ipopt 3.13.2: nlp_scaling_method=gradient-based\n",
+ "tol=1e-06\n",
+ "max_iter=200\n",
+ "\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...: 1097\n",
+ "Number of nonzeros in inequality constraint Jacobian.: 0\n",
+ "Number of nonzeros in Lagrangian Hessian.............: 877\n",
+ "\n",
+ "Total number of variables............................: 363\n",
+ " variables with only lower bounds: 8\n",
+ " variables with lower and upper bounds: 155\n",
+ " variables with only upper bounds: 0\n",
+ "Total number of equality constraints.................: 363\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.82e+04 1.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
+ " 1 0.0000000e+00 8.69e+03 1.44e+03 -1.0 2.00e+04 - 9.71e-01 4.67e-01H 1\n",
+ " 2 0.0000000e+00 1.29e+03 1.56e+03 -1.0 1.60e+04 - 9.79e-01 4.90e-01h 1\n",
+ " 3 0.0000000e+00 1.18e+03 1.55e+05 -1.0 1.40e+04 - 9.90e-01 4.99e-01h 1\n",
+ " 4 0.0000000e+00 5.46e+02 2.32e+09 -1.0 8.42e+03 - 1.00e+00 9.82e-01h 1\n",
+ " 5 0.0000000e+00 5.46e+03 3.66e+10 -1.0 5.97e+02 - 1.00e+00 9.90e-01h 1\n",
+ " 6 0.0000000e+00 1.21e+03 8.01e+09 -1.0 5.75e+00 - 1.00e+00 1.00e+00h 1\n",
+ " 7 0.0000000e+00 6.41e+00 3.87e+07 -1.0 1.53e-03 - 1.00e+00 1.00e+00f 1\n",
+ " 8 0.0000000e+00 1.96e-04 9.36e+02 -1.0 7.28e-06 - 1.00e+00 1.00e+00h 1\n",
+ " 9 0.0000000e+00 2.24e-08 4.99e-01 -3.8 5.92e-08 - 1.00e+00 1.00e+00h 1\n",
+ "Cannot recompute multipliers for feasibility problem. Error in eq_mult_calculator\n",
+ "\n",
+ "Number of Iterations....: 9\n",
+ "\n",
+ " (scaled) (unscaled)\n",
+ "Objective...............: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Dual infeasibility......: 1.5042487592972509e+04 1.5042487592972509e+04\n",
+ "Constraint violation....: 2.9103830456733704e-11 2.2351741790771484e-08\n",
+ "Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Overall NLP error.......: 2.9103830456733704e-11 1.5042487592972509e+04\n",
+ "\n",
+ "\n",
+ "Number of objective function evaluations = 11\n",
+ "Number of objective gradient evaluations = 10\n",
+ "Number of equality constraint evaluations = 11\n",
+ "Number of inequality constraint evaluations = 0\n",
+ "Number of equality constraint Jacobian evaluations = 10\n",
+ "Number of inequality constraint Jacobian evaluations = 0\n",
+ "Number of Lagrangian Hessian evaluations = 9\n",
+ "Total CPU secs in IPOPT (w/o function evaluations) = 0.011\n",
+ "Total CPU secs in NLP function evaluations = 0.001\n",
+ "\n",
+ "EXIT: Optimal Solution Found.\n"
+ ]
+ }
+ ],
"source": [
"# Create the solver object\n",
"solver = get_solver()\n",
@@ -1205,7 +1493,7 @@
"- Add the distillation column \n",
"- Connect it to the heater \n",
"- Add the necessary equality constraints\n",
- "- Propogate the state variable information from the outlet of the heater to the inlet of the distillation column \n",
+ "- Propagate the state variable information from the outlet of the heater to the inlet of the distillation column \n",
"- Fix the degrees of freedom of the distillation block (reflux ratio, boilup ratio, and condenser pressure)\n",
"- Scale the control volume heat variables to help convergence\n",
"- Initialize the distillation block.\n",
@@ -1216,7 +1504,617 @@
"cell_type": "code",
"execution_count": 50,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].material_flow_terms[Liq,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].material_flow_terms[Vap,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].material_flow_terms[Liq,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].material_flow_terms[Vap,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].enthalpy_flow_terms[Liq], enthalpy_flow_terms\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].enthalpy_flow_terms[Vap], enthalpy_flow_terms\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].material_flow_terms[Liq,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].material_flow_terms[Vap,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].material_flow_terms[Liq,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].material_flow_terms[Vap,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].enthalpy_flow_terms[Liq], enthalpy_flow_terms\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].enthalpy_flow_terms[Vap], enthalpy_flow_terms\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.e_flow_reflux[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.e_mole_frac_reflux[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.e_mole_frac_reflux[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.e_flow_vapor_reboil[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.e_mole_frac_vapor_reboil[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.e_mole_frac_vapor_reboil[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101: Begin initialization.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray: Begin initialization.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: State Released.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: State Released.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser.control_volume: Initialization Complete\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser: Initialization Complete, optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: State Released.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: State Released.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler: Initialization Complete, optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: State Released.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.rectification_section[1]: Begin initialization.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: State Released.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[2]: Begin initialization.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: State Released.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[2]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[2]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3]: Begin initialization.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: State Released.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[3]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[3]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[3]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4]: Begin initialization.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: State Released.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.stripping_section[6]: Begin initialization.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: State Released.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[7]: Begin initialization.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: State Released.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[7]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[7]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8]: Begin initialization.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: State Released.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[8]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[8]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[8]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9]: Begin initialization.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: State Released.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10]: Begin initialization.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: State Released.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: State Released.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101: Rectification section initialization status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101: Stripping section initialization status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: State Released.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: State Released.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101: Column section initialization status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: State Released.\n",
+ "2024-08-28 18:39:02 [INFO] idaes.init.fs.D101: Column section + Condenser initialization status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:02 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: State Released.\n",
+ "2024-08-28 18:39:03 [INFO] idaes.init.fs.D101: Column section + Condenser + Reboiler initialization status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:03 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: State Released.\n"
+ ]
+ }
+ ],
"source": [
"# Add distillation column to the flowsheet\n",
"m.fs.D101 = TrayColumn(\n",
@@ -1315,7 +2213,101 @@
"cell_type": "code",
"execution_count": 53,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "WARNING: model contains export suffix 'scaling_factor' that contains 7\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 150 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "Ipopt 3.13.2: nlp_scaling_method=gradient-based\n",
+ "tol=1e-06\n",
+ "max_iter=200\n",
+ "\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...: 4042\n",
+ "Number of nonzeros in inequality constraint Jacobian.: 0\n",
+ "Number of nonzeros in Lagrangian Hessian.............: 2376\n",
+ "\n",
+ "Total number of variables............................: 1169\n",
+ " variables with only lower bounds: 112\n",
+ " variables with lower and upper bounds: 365\n",
+ " variables with only upper bounds: 0\n",
+ "Total number of equality constraints.................: 1169\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.83e+04 1.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
+ " 1 0.0000000e+00 8.70e+03 1.50e+03 -1.0 3.69e+04 - 9.71e-01 4.62e-01H 1\n",
+ " 2 0.0000000e+00 1.53e+03 1.56e+03 -1.0 6.75e+03 - 9.77e-01 4.89e-01h 1\n",
+ " 3 0.0000000e+00 1.37e+03 1.55e+05 -1.0 9.37e+03 - 9.90e-01 4.99e-01h 1\n",
+ " 4 0.0000000e+00 6.14e+02 2.31e+09 -1.0 6.09e+03 - 1.00e+00 9.81e-01h 1\n",
+ " 5 0.0000000e+00 5.32e+03 3.62e+10 -1.0 5.56e+02 - 1.00e+00 9.90e-01h 1\n",
+ " 6 0.0000000e+00 1.16e+03 7.80e+09 -1.0 5.36e+00 - 1.00e+00 1.00e+00h 1\n",
+ " 7 0.0000000e+00 5.96e+00 3.64e+07 -1.0 1.47e-03 - 1.00e+00 1.00e+00f 1\n",
+ " 8 0.0000000e+00 1.69e-04 8.15e+02 -1.0 6.77e-06 - 1.00e+00 1.00e+00h 1\n",
+ " 9 0.0000000e+00 7.45e-09 6.64e-03 -3.8 2.00e-07 - 1.00e+00 1.00e+00h 1\n",
+ "Cannot recompute multipliers for feasibility problem. Error in eq_mult_calculator\n",
+ "\n",
+ "Number of Iterations....: 9\n",
+ "\n",
+ " (scaled) (unscaled)\n",
+ "Objective...............: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Dual infeasibility......: 1.5042483516409773e+04 1.5042483516409773e+04\n",
+ "Constraint violation....: 2.9103830456733704e-11 7.4505805969238281e-09\n",
+ "Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Overall NLP error.......: 2.9103830456733704e-11 1.5042483516409773e+04\n",
+ "\n",
+ "\n",
+ "Number of objective function evaluations = 11\n",
+ "Number of objective gradient evaluations = 10\n",
+ "Number of equality constraint evaluations = 11\n",
+ "Number of inequality constraint evaluations = 0\n",
+ "Number of equality constraint Jacobian evaluations = 10\n",
+ "Number of inequality constraint Jacobian evaluations = 0\n",
+ "Number of Lagrangian Hessian evaluations = 9\n",
+ "Total CPU secs in IPOPT (w/o function evaluations) = 0.083\n",
+ "Total CPU secs in NLP function evaluations = 0.013\n",
+ "\n",
+ "EXIT: Optimal Solution Found.\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "{'Problem': [{'Lower bound': -inf, 'Upper bound': inf, 'Number of objectives': 1, 'Number of constraints': 1169, 'Number of variables': 1169, 'Sense': 'unknown'}], 'Solver': [{'Status': 'ok', 'Message': 'Ipopt 3.13.2\\\\x3a Optimal Solution Found', 'Termination condition': 'optimal', 'Id': 0, 'Error rc': 0, 'Time': 0.2022566795349121}], 'Solution': [OrderedDict([('number of solutions', 0), ('number of solutions displayed', 0)])]}"
+ ]
+ },
+ "execution_count": 53,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
"source": [
"solver.solve(m, tee=True)"
]
@@ -1349,7 +2341,26 @@
"cell_type": "code",
"execution_count": 55,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "total cost = $ 442301.47075252194\n",
+ "operating cost = $ 427596.73056805483\n",
+ "capital cost = $ 14704.740184467111\n",
+ "\n",
+ "Distillate flowrate = 0.16196898920633368 mol/s\n",
+ "Benzene purity = 89.4916166580088 %\n",
+ "Residue flowrate = 0.10515007120697904 mol/s\n",
+ "Toluene purity = 43.32260291055251 %\n",
+ "\n",
+ "Conversion = 75.0 %\n",
+ "\n",
+ "Overhead benzene loss in F101 = 42.161938483603194 %\n"
+ ]
+ }
+ ],
"source": [
"print(\"total cost = $\", value(m.fs.capital_cost) + value(m.fs.operating_cost))\n",
"print(\"operating cost = $\", value(m.fs.operating_cost))\n",
@@ -1391,7 +2402,16 @@
"testing"
]
},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "427596.73056805483\n",
+ "14704.740184467111\n"
+ ]
+ }
+ ],
"source": [
"import pytest\n",
"\n",
@@ -1412,7 +2432,40 @@
"cell_type": "code",
"execution_count": 57,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "====================================================================================\n",
+ "Unit : fs.R101 Time: 0.0\n",
+ "------------------------------------------------------------------------------------\n",
+ " Unit Performance\n",
+ "\n",
+ " Variables: \n",
+ "\n",
+ " Key : Value : Units : Fixed : Bounds\n",
+ " Heat Duty : 0.0000 : watt : True : (None, None)\n",
+ " Volume : 0.14705 : meter ** 3 : False : (None, None)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ " Stream Table\n",
+ " Units Inlet Outlet \n",
+ " flow_mol_phase_comp ('Liq', 'benzene') mole / second 1.2993e-07 1.2993e-07\n",
+ " flow_mol_phase_comp ('Liq', 'toluene') mole / second 8.4147e-07 8.4147e-07\n",
+ " flow_mol_phase_comp ('Liq', 'methane') mole / second 1.0000e-12 1.0000e-12\n",
+ " flow_mol_phase_comp ('Liq', 'hydrogen') mole / second 1.0000e-12 1.0000e-12\n",
+ " flow_mol_phase_comp ('Vap', 'benzene') mole / second 0.11936 0.35374\n",
+ " flow_mol_phase_comp ('Vap', 'toluene') mole / second 0.31252 0.078129\n",
+ " flow_mol_phase_comp ('Vap', 'methane') mole / second 1.0377 1.2721\n",
+ " flow_mol_phase_comp ('Vap', 'hydrogen') mole / second 0.56260 0.32821\n",
+ " temperature kelvin 600.00 771.85\n",
+ " pressure pascal 3.5000e+05 3.5000e+05\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
"source": [
"m.fs.R101.report()"
]
@@ -1428,7 +2481,40 @@
"cell_type": "code",
"execution_count": 58,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "====================================================================================\n",
+ "Unit : fs.F101 Time: 0.0\n",
+ "------------------------------------------------------------------------------------\n",
+ " Unit Performance\n",
+ "\n",
+ " Variables: \n",
+ "\n",
+ " Key : Value : Units : Fixed : Bounds\n",
+ " Heat Duty : -70343. : watt : False : (None, None)\n",
+ " Pressure Change : 0.0000 : pascal : True : (None, None)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ " Stream Table\n",
+ " Units Inlet Vapor Outlet Liquid Outlet\n",
+ " flow_mol_phase_comp ('Liq', 'benzene') mole / second 1.2993e-07 1.0000e-08 0.20460 \n",
+ " flow_mol_phase_comp ('Liq', 'toluene') mole / second 8.4147e-07 1.0000e-08 0.062520 \n",
+ " flow_mol_phase_comp ('Liq', 'methane') mole / second 1.0000e-12 1.0000e-08 2.6712e-07 \n",
+ " flow_mol_phase_comp ('Liq', 'hydrogen') mole / second 1.0000e-12 1.0000e-08 2.6712e-07 \n",
+ " flow_mol_phase_comp ('Vap', 'benzene') mole / second 0.35374 0.14915 1.0000e-08 \n",
+ " flow_mol_phase_comp ('Vap', 'toluene') mole / second 0.078129 0.015610 1.0000e-08 \n",
+ " flow_mol_phase_comp ('Vap', 'methane') mole / second 1.2721 1.2721 1.0000e-08 \n",
+ " flow_mol_phase_comp ('Vap', 'hydrogen') mole / second 0.32821 0.32821 1.0000e-08 \n",
+ " temperature kelvin 771.85 325.00 325.00 \n",
+ " pressure pascal 3.5000e+05 3.5000e+05 3.5000e+05 \n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
"source": [
"m.fs.F101.report()"
]
@@ -1449,7 +2535,25 @@
"cell_type": "code",
"execution_count": 59,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ " Units Reactor Light Gases\n",
+ "flow_mol_phase_comp ('Liq', 'benzene') mole / second 1.2993e-07 1.0000e-08 \n",
+ "flow_mol_phase_comp ('Liq', 'toluene') mole / second 8.4147e-07 1.0000e-08 \n",
+ "flow_mol_phase_comp ('Liq', 'methane') mole / second 1.0000e-12 1.0000e-08 \n",
+ "flow_mol_phase_comp ('Liq', 'hydrogen') mole / second 1.0000e-12 1.0000e-08 \n",
+ "flow_mol_phase_comp ('Vap', 'benzene') mole / second 0.35374 0.14915 \n",
+ "flow_mol_phase_comp ('Vap', 'toluene') mole / second 0.078129 0.015610 \n",
+ "flow_mol_phase_comp ('Vap', 'methane') mole / second 1.2721 1.2721 \n",
+ "flow_mol_phase_comp ('Vap', 'hydrogen') mole / second 0.32821 0.32821 \n",
+ "temperature kelvin 771.85 325.00 \n",
+ "pressure pascal 3.5000e+05 3.5000e+05 \n"
+ ]
+ }
+ ],
"source": [
"from idaes.core.util.tables import (\n",
" create_stream_table_dataframe,\n",
@@ -1466,7 +2570,7 @@
"source": [
"\n",
"Inline Exercise:\n",
- "You can querry additional variables here if you like. \n",
+ "You can query additional variables here if you like. \n",
"\n",
"Use Shift+Enter to run the cell once you have typed in your code. \n",
"
"
@@ -1756,7 +2860,117 @@
"cell_type": "code",
"execution_count": 71,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "WARNING: model contains export suffix 'scaling_factor' that contains 3\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 150 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "Ipopt 3.13.2: nlp_scaling_method=gradient-based\n",
+ "tol=1e-06\n",
+ "max_iter=200\n",
+ "\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...: 4073\n",
+ "Number of nonzeros in inequality constraint Jacobian.: 6\n",
+ "Number of nonzeros in Lagrangian Hessian.............: 2391\n",
+ "\n",
+ "Total number of variables............................: 1176\n",
+ " variables with only lower bounds: 113\n",
+ " variables with lower and upper bounds: 372\n",
+ " variables with only upper bounds: 0\n",
+ "Total number of equality constraints.................: 1169\n",
+ "Total number of inequality constraints...............: 3\n",
+ " inequality constraints with only lower bounds: 2\n",
+ " inequality constraints with lower and upper bounds: 0\n",
+ " inequality constraints with only upper bounds: 1\n",
+ "\n",
+ "iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n",
+ " 0 4.4230147e+05 2.99e+05 9.90e+01 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
+ " 1 4.3753585e+05 2.91e+05 1.28e+02 -1.0 3.09e+06 - 3.58e-01 2.40e-02f 1\n",
+ " 2 4.3545100e+05 2.78e+05 1.55e+02 -1.0 1.78e+06 - 3.31e-01 4.74e-02h 1\n",
+ " 3 4.2822311e+05 2.20e+05 4.50e+02 -1.0 2.99e+06 - 2.95e-02 1.35e-01h 1\n",
+ " 4 4.2249096e+05 1.45e+05 1.43e+03 -1.0 7.01e+06 - 5.14e-01 2.03e-01h 1\n",
+ " 5 4.2194364e+05 8.17e+04 1.70e+04 -1.0 6.06e+06 - 5.97e-01 4.28e-01h 1\n",
+ " 6 4.2602765e+05 4.55e+04 1.10e+06 -1.0 4.32e+06 - 9.26e-01 5.07e-01h 1\n",
+ " 7 4.3776643e+05 2.03e+04 6.44e+09 -1.0 2.42e+06 - 9.90e-01 9.47e-01h 1\n",
+ " 8 4.3846260e+05 1.92e+04 6.05e+09 -1.0 4.42e+05 - 5.40e-01 5.74e-02h 1\n",
+ " 9 4.4529853e+05 4.05e+04 4.66e+10 -1.0 2.47e+05 - 9.96e-01 9.90e-01h 1\n",
+ "iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n",
+ " 10 4.4906283e+05 9.76e+03 1.10e+10 -1.0 1.12e+03 -4.0 1.26e-01 7.45e-01h 1\n",
+ " 11 4.5079086e+05 1.19e+03 1.54e+09 -1.0 5.63e+02 -4.5 3.77e-01 1.00e+00h 1\n",
+ " 12 4.5024224e+05 2.66e+00 3.67e+06 -1.0 6.61e+01 -5.0 1.00e+00 1.00e+00f 1\n",
+ " 13 4.4946170e+05 5.64e-01 9.29e+05 -1.0 1.81e+02 -5.4 1.00e+00 7.88e-01f 1\n",
+ " 14 4.4916780e+05 8.48e+00 1.62e+05 -1.0 2.83e+02 -5.9 1.00e+00 1.00e+00f 1\n",
+ " 15 4.4899127e+05 4.83e+00 9.07e+04 -1.0 1.01e+02 -6.4 1.00e+00 4.40e-01f 2\n",
+ " 16 4.4886718e+05 7.00e-01 4.61e+02 -1.0 2.35e+02 -6.9 1.00e+00 1.00e+00f 1\n",
+ " 17 4.4800159e+05 1.39e+02 4.52e+06 -3.8 1.17e+03 -7.3 9.79e-01 9.37e-01f 1\n",
+ " 18 4.4672196e+05 9.59e+02 1.22e+06 -3.8 4.55e+03 -7.8 1.00e+00 9.43e-01f 1\n",
+ " 19 4.4401667e+05 7.75e+03 1.55e+05 -3.8 1.08e+04 -8.3 1.00e+00 1.00e+00f 1\n",
+ "iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n",
+ " 20 4.4185035e+05 1.91e+04 1.36e+04 -3.8 1.33e+04 -8.8 1.00e+00 1.00e+00h 1\n",
+ " 21 4.4241001e+05 3.52e+03 5.96e+03 -3.8 2.94e+03 -9.2 1.00e+00 1.00e+00h 1\n",
+ " 22 4.4185237e+05 7.82e+00 2.91e+02 -3.8 7.13e+03 -9.7 2.39e-01 1.00e+00h 1\n",
+ " 23 4.4124091e+05 1.53e+01 3.11e+02 -3.8 4.82e+04 -10.2 8.59e-01 1.36e-01f 1\n",
+ " 24 4.4137379e+05 1.80e+00 2.91e+02 -3.8 1.41e+04 - 1.95e-01 1.00e+00h 1\n",
+ " 25 4.3862833e+05 1.70e+03 9.48e+04 -3.8 1.57e+07 - 1.29e-03 9.10e-02f 1\n",
+ " 26 4.3883308e+05 1.49e+03 8.46e+04 -3.8 1.02e+06 - 1.00e+00 1.35e-01h 1\n",
+ " 27 4.3885472e+05 2.18e+01 3.40e+03 -3.8 1.38e+05 - 1.00e+00 1.00e+00h 1\n",
+ " 28 4.3884160e+05 5.90e-02 6.38e+01 -3.8 8.66e+03 - 1.00e+00 1.00e+00h 1\n",
+ " 29 4.3884157e+05 6.48e-07 4.63e-04 -3.8 2.89e+01 - 1.00e+00 1.00e+00h 1\n",
+ "iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n",
+ " 30 4.3883990e+05 3.57e-01 2.38e+03 -5.7 8.19e+02 - 1.00e+00 1.00e+00f 1\n",
+ " 31 4.3883992e+05 3.50e-07 7.79e-06 -5.7 3.55e-01 - 1.00e+00 1.00e+00h 1\n",
+ " 32 4.3883990e+05 5.47e-05 3.63e-01 -8.0 1.01e+01 - 1.00e+00 1.00e+00h 1\n",
+ " 33 4.3883990e+05 2.24e-08 1.46e-07 -8.0 5.42e-05 - 1.00e+00 1.00e+00h 1\n",
+ "\n",
+ "Number of Iterations....: 33\n",
+ "\n",
+ " (scaled) (unscaled)\n",
+ "Objective...............: 4.3883989842628603e+02 4.3883989842628600e+05\n",
+ "Dual infeasibility......: 1.4600704448671754e-07 1.4600704448671753e-04\n",
+ "Constraint violation....: 2.9103830456733704e-11 2.2351741790771484e-08\n",
+ "Complementarity.........: 9.0909948039799681e-09 9.0909948039799686e-06\n",
+ "Overall NLP error.......: 9.0909948039799681e-09 1.4600704448671753e-04\n",
+ "\n",
+ "\n",
+ "Number of objective function evaluations = 35\n",
+ "Number of objective gradient evaluations = 34\n",
+ "Number of equality constraint evaluations = 35\n",
+ "Number of inequality constraint evaluations = 35\n",
+ "Number of equality constraint Jacobian evaluations = 34\n",
+ "Number of inequality constraint Jacobian evaluations = 34\n",
+ "Number of Lagrangian Hessian evaluations = 33\n",
+ "Total CPU secs in IPOPT (w/o function evaluations) = 0.164\n",
+ "Total CPU secs in NLP function evaluations = 0.020\n",
+ "\n",
+ "EXIT: Optimal Solution Found.\n"
+ ]
+ }
+ ],
"source": [
"results = solver.solve(m, tee=True)"
]
@@ -1790,7 +3004,26 @@
"cell_type": "code",
"execution_count": 73,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "total cost = $ 438839.898426286\n",
+ "operating cost = $ 408883.5314830889\n",
+ "capital cost = $ 29956.3669431971\n",
+ "\n",
+ "Distillate flowrate = 0.1799999900263989 mol/s\n",
+ "Benzene purity = 98.99999900049086 %\n",
+ "Residue flowrate = 0.1085161642426372 mol/s\n",
+ "Toluene purity = 15.676178086213548 %\n",
+ "\n",
+ "Conversion = 93.38705916369427 %\n",
+ "\n",
+ "Overhead benzene loss in F101 = 17.34061793115618 %\n"
+ ]
+ }
+ ],
"source": [
"print(\"total cost = $\", value(m.fs.capital_cost) + value(m.fs.operating_cost))\n",
"print(\"operating cost = $\", value(m.fs.operating_cost))\n",
@@ -1832,7 +3065,16 @@
"testing"
]
},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "408883.5314830889\n",
+ "29956.3669431971\n"
+ ]
+ }
+ ],
"source": [
"import pytest\n",
"\n",
@@ -1854,7 +3096,23 @@
"cell_type": "code",
"execution_count": 75,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Optimal Values\n",
+ "\n",
+ "H101 outlet temperature = 568.923204295196 K\n",
+ "\n",
+ "R101 outlet temperature = 790.3655425698853 K\n",
+ "\n",
+ "F101 outlet temperature = 298.0 K\n",
+ "\n",
+ "H102 outlet temperature = 368.7414339952852 K\n"
+ ]
+ }
+ ],
"source": [
"print(\"Optimal Values\")\n",
"print()\n",
@@ -1884,13 +3142,6 @@
"\n",
"Finally, observe that the operating temperature of the flash (F101) is almost at its lower bound. This helps in minimizing the amount of benzene in the vapor stream leaving the flash."
]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
}
],
"metadata": {
@@ -1910,7 +3161,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.8.16"
+ "version": "3.10.13"
}
},
"nbformat": 4,
diff --git a/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_distillation_doc.ipynb b/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_distillation_doc.ipynb
index b719e1e8..1e23d2be 100644
--- a/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_distillation_doc.ipynb
+++ b/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_distillation_doc.ipynb
@@ -144,7 +144,7 @@
},
{
"cell_type": "code",
- "execution_count": 3,
+ "execution_count": 4,
"metadata": {
"tags": [
"solution"
@@ -175,7 +175,7 @@
},
{
"cell_type": "code",
- "execution_count": 4,
+ "execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
@@ -184,7 +184,7 @@
},
{
"cell_type": "code",
- "execution_count": 5,
+ "execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
@@ -211,7 +211,7 @@
},
{
"cell_type": "code",
- "execution_count": 6,
+ "execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
@@ -221,6 +221,7 @@
"from idaes.core.util.initialization import propagate_state\n",
"from idaes.core.solvers import get_solver\n",
"import idaes.core.util.scaling as iscale\n",
+ "from idaes.core.util.exceptions import InitializationError\n",
"\n",
"# Import idaes logger to set output levels\n",
"import idaes.logger as idaeslog"
@@ -237,7 +238,7 @@
},
{
"cell_type": "code",
- "execution_count": 7,
+ "execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
@@ -260,7 +261,7 @@
},
{
"cell_type": "code",
- "execution_count": 8,
+ "execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
@@ -280,7 +281,7 @@
},
{
"cell_type": "code",
- "execution_count": 9,
+ "execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
@@ -309,7 +310,7 @@
},
{
"cell_type": "code",
- "execution_count": 10,
+ "execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
@@ -341,7 +342,7 @@
},
{
"cell_type": "code",
- "execution_count": 11,
+ "execution_count": 13,
"metadata": {
"tags": [
"solution"
@@ -367,7 +368,7 @@
},
{
"cell_type": "code",
- "execution_count": 12,
+ "execution_count": 14,
"metadata": {},
"outputs": [],
"source": [
@@ -407,7 +408,7 @@
},
{
"cell_type": "code",
- "execution_count": 13,
+ "execution_count": 15,
"metadata": {},
"outputs": [],
"source": [
@@ -436,7 +437,7 @@
},
{
"cell_type": "code",
- "execution_count": 14,
+ "execution_count": 16,
"metadata": {},
"outputs": [],
"source": [
@@ -472,7 +473,7 @@
},
{
"cell_type": "code",
- "execution_count": 15,
+ "execution_count": 18,
"metadata": {
"tags": [
"solution"
@@ -488,7 +489,7 @@
},
{
"cell_type": "code",
- "execution_count": 16,
+ "execution_count": 19,
"metadata": {},
"outputs": [],
"source": [
@@ -527,7 +528,7 @@
},
{
"cell_type": "code",
- "execution_count": 17,
+ "execution_count": 21,
"metadata": {
"tags": [
"solution"
@@ -549,12 +550,12 @@
"source": [
"## Connecting Unit Models using Arcs\n",
"\n",
- "We have now added the initial set of unit models to the flowsheet. However, we have not yet specifed how the units are connected. To do this, we will be using the `Arc` which is a pyomo component that takes in two arguments: `source` and `destination`. Let us connect the outlet of the mixer (M101) to the inlet of the heater (H101). "
+ "We have now added the initial set of unit models to the flowsheet. However, we have not yet specified how the units are connected. To do this, we will be using the `Arc` which is a pyomo component that takes in two arguments: `source` and `destination`. Let us connect the outlet of the mixer (M101) to the inlet of the heater (H101). "
]
},
{
"cell_type": "code",
- "execution_count": 18,
+ "execution_count": 22,
"metadata": {},
"outputs": [],
"source": [
@@ -577,7 +578,7 @@
},
{
"cell_type": "code",
- "execution_count": 19,
+ "execution_count": 24,
"metadata": {
"tags": [
"solution"
@@ -598,7 +599,7 @@
},
{
"cell_type": "code",
- "execution_count": 20,
+ "execution_count": 25,
"metadata": {},
"outputs": [],
"source": [
@@ -619,7 +620,7 @@
},
{
"cell_type": "code",
- "execution_count": 21,
+ "execution_count": 26,
"metadata": {},
"outputs": [],
"source": [
@@ -642,7 +643,7 @@
},
{
"cell_type": "code",
- "execution_count": 22,
+ "execution_count": 27,
"metadata": {},
"outputs": [],
"source": [
@@ -670,7 +671,7 @@
},
{
"cell_type": "code",
- "execution_count": 23,
+ "execution_count": 28,
"metadata": {},
"outputs": [
{
@@ -694,7 +695,7 @@
},
{
"cell_type": "code",
- "execution_count": 24,
+ "execution_count": 30,
"metadata": {},
"outputs": [],
"source": [
@@ -726,7 +727,7 @@
},
{
"cell_type": "code",
- "execution_count": 25,
+ "execution_count": 31,
"metadata": {},
"outputs": [],
"source": [
@@ -753,7 +754,7 @@
},
{
"cell_type": "code",
- "execution_count": 26,
+ "execution_count": 32,
"metadata": {},
"outputs": [],
"source": [
@@ -779,7 +780,7 @@
},
{
"cell_type": "code",
- "execution_count": 27,
+ "execution_count": 34,
"metadata": {
"tags": [
"solution"
@@ -803,7 +804,7 @@
},
{
"cell_type": "code",
- "execution_count": 28,
+ "execution_count": 35,
"metadata": {},
"outputs": [],
"source": [
@@ -823,7 +824,7 @@
},
{
"cell_type": "code",
- "execution_count": 29,
+ "execution_count": 36,
"metadata": {},
"outputs": [],
"source": [
@@ -843,7 +844,7 @@
},
{
"cell_type": "code",
- "execution_count": 30,
+ "execution_count": 37,
"metadata": {},
"outputs": [],
"source": [
@@ -863,203 +864,41 @@
},
{
"cell_type": "code",
- "execution_count": 31,
+ "execution_count": 38,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "2023-11-02 10:27:18 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:18 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enth_mol_phase_comp[Liq,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:18 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enth_mol_phase_comp[Liq,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:18 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enth_mol_phase_comp[Vap,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:18 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enth_mol_phase_comp[Vap,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:18 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:18 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:18 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:18 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:18 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:18 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:18 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:18 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].enth_mol_phase_comp[Liq,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:18 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].enth_mol_phase_comp[Liq,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:18 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].enth_mol_phase_comp[Vap,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:18 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].enth_mol_phase_comp[Vap,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:18 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:18 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:18 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:18 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:18 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:18 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:18 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].material_flow_terms[Liq,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:18 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].material_flow_terms[Vap,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:18 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].material_flow_terms[Liq,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:18 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].material_flow_terms[Vap,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:18 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enthalpy_flow_terms[Liq], enthalpy_flow_terms\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:18 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enthalpy_flow_terms[Vap], enthalpy_flow_terms\n"
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enth_mol_phase_comp[Liq,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enth_mol_phase_comp[Liq,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enth_mol_phase_comp[Vap,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enth_mol_phase_comp[Vap,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].enth_mol_phase_comp[Liq,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].enth_mol_phase_comp[Liq,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].enth_mol_phase_comp[Vap,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].enth_mol_phase_comp[Vap,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].material_flow_terms[Liq,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].material_flow_terms[Vap,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].material_flow_terms[Liq,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].material_flow_terms[Vap,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enthalpy_flow_terms[Liq], enthalpy_flow_terms\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enthalpy_flow_terms[Vap], enthalpy_flow_terms\n"
]
}
],
@@ -1093,7 +932,7 @@
},
{
"cell_type": "code",
- "execution_count": 32,
+ "execution_count": 40,
"metadata": {
"tags": [
"solution"
@@ -1126,7 +965,7 @@
},
{
"cell_type": "code",
- "execution_count": 33,
+ "execution_count": 42,
"metadata": {},
"outputs": [],
"source": [
@@ -1150,7 +989,7 @@
},
{
"cell_type": "code",
- "execution_count": 34,
+ "execution_count": 43,
"metadata": {},
"outputs": [
{
@@ -1175,7 +1014,7 @@
},
{
"cell_type": "code",
- "execution_count": 35,
+ "execution_count": 44,
"metadata": {},
"outputs": [
{
@@ -1207,7 +1046,7 @@
},
{
"cell_type": "code",
- "execution_count": 36,
+ "execution_count": 45,
"metadata": {},
"outputs": [],
"source": [
@@ -1222,7 +1061,7 @@
" (0, \"Liq\", \"hydrogen\"): 1e-5,\n",
" (0, \"Liq\", \"methane\"): 1e-5,\n",
" },\n",
- " \"temperature\": {0: 303},\n",
+ " \"temperature\": {0: 303.2},\n",
" \"pressure\": {0: 350000},\n",
"}\n",
"\n",
@@ -1234,17 +1073,24 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "Next, we need to tell the tool how to initialize a particular unit. We will be writing a python function which takes in a \"unit\" and calls the initialize method on that unit."
+ "Next, we need to tell the tool how to initialize a particular unit. We will be writing a python function which takes in a \"unit\" and calls the initialize method on that unit. For the initialization, we will import a Block Triangularization Initializer which decomposes the model into a set of subproblems. These subproblems are solved using a block triangularization transformation before applying a simple Newton or user-selected solver. Methods such as block triangularization often solve faster and yield more reliable behavior than heuristic methods, but sometime struggle to decompose models with strongly coupled equations (e.g. column models, systems with counter-current flow, vapor-liquid equilibrium)."
]
},
{
"cell_type": "code",
- "execution_count": 37,
+ "execution_count": 46,
"metadata": {},
"outputs": [],
"source": [
"def function(unit):\n",
- " unit.initialize(outlvl=idaeslog.INFO)"
+ " # Try initializing using default initializer,\n",
+ " # if it fails (probably due to scaling) try for the second time\n",
+ " try:\n",
+ " initializer = unit.default_initializer()\n",
+ " initializer.initialize(unit, output_level=idaeslog.INFO)\n",
+ " except InitializationError:\n",
+ " solver = get_solver()\n",
+ " solver.solve(unit)"
]
},
{
@@ -1256,7 +1102,7 @@
},
{
"cell_type": "code",
- "execution_count": 38,
+ "execution_count": 47,
"metadata": {
"scrolled": false
},
@@ -1265,5219 +1111,869 @@
"name": "stdout",
"output_type": "stream",
"text": [
- "2023-11-02 10:27:19 [INFO] idaes.init.fs.H101.control_volume: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:19 [INFO] idaes.init.fs.H101: Initialization Complete: optimal - Optimal Solution Found\n"
+ "2024-08-28 18:38:14 [INFO] idaes.init.fs.H101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:16 [INFO] idaes.init.fs.H101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:16 [INFO] idaes.init.fs.R101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:16 [INFO] idaes.init.fs.R101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.F101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.F101.control_volume.properties_out: Initialization Complete\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 12\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 50 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.S101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - \n",
+ "2024-08-28 18:38:18 [INFO] idaes.init.fs.C101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:18 [INFO] idaes.init.fs.C101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:18 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 1 optimal - .\n",
+ "2024-08-28 18:38:18 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 2 optimal - .\n",
+ "2024-08-28 18:38:19 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 3 optimal - .\n",
+ "2024-08-28 18:38:19 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 4 optimal - .\n",
+ "2024-08-28 18:38:19 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 5 optimal - .\n",
+ "2024-08-28 18:38:19 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 1 optimal - .\n",
+ "2024-08-28 18:38:19 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 2 optimal - .\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 3 optimal - .\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 4 optimal - .\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 5 optimal - .\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.M101.toluene_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.M101.hydrogen_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.M101.vapor_recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:21 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:21 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - \n",
+ "2024-08-28 18:38:21 [INFO] idaes.init.fs.H101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:21 [INFO] idaes.init.fs.H101.control_volume.properties_out: Initialization Complete\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 11\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 50 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.R101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.R101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.F101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.F101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.S101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - \n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.C101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.C101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.M101.toluene_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.M101.hydrogen_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.M101.vapor_recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - \n",
+ "2024-08-28 18:38:24 [INFO] idaes.init.fs.H101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:24 [INFO] idaes.init.fs.H101.control_volume.properties_out: Initialization Complete\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 11\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 50 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "2024-08-28 18:38:24 [INFO] idaes.init.fs.R101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:24 [INFO] idaes.init.fs.R101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.F101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.F101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.S101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - \n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.C101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.C101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.M101.toluene_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.M101.hydrogen_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.M101.vapor_recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - \n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.H101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.H101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.R101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.R101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.F101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.F101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.S101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - \n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.C101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.C101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.M101.toluene_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.M101.hydrogen_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.M101.vapor_recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - \n",
+ "2024-08-28 18:38:29 [INFO] idaes.init.fs.H101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:29 [INFO] idaes.init.fs.H101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:29 [INFO] idaes.init.fs.R101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:29 [INFO] idaes.init.fs.R101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.F101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.F101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.S101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - \n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.C101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.C101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.M101.toluene_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.M101.hydrogen_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:31 [INFO] idaes.init.fs.M101.vapor_recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:31 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:31 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - \n",
+ "WARNING: Wegstein failed to converge in 3 iterations\n",
+ "2024-08-28 18:38:31 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 1 optimal - .\n",
+ "2024-08-28 18:38:31 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 2 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 3 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 4 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 5 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 1 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 2 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 3 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 4 optimal - .\n",
+ "2024-08-28 18:38:33 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 5 optimal - .\n"
]
- },
+ }
+ ],
+ "source": [
+ "seq.run(m, function)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "We have now initialized the flowsheet. Let us run the flowsheet in a simulation mode to look at the results. \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 48,
+ "metadata": {},
+ "outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "2023-11-02 10:27:19 [INFO] idaes.init.fs.R101.control_volume: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:19 [INFO] idaes.init.fs.R101: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:19 [INFO] idaes.init.fs.F101.control_volume: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:19 [INFO] idaes.init.fs.F101: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:19 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:19 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:19 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:19 [INFO] idaes.init.fs.translator.properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:19 [INFO] idaes.init.fs.translator.properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:19 [INFO] idaes.init.fs.translator.properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:19 [INFO] idaes.init.fs.translator.properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:20 [INFO] idaes.init.fs.translator.properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:20 [INFO] idaes.init.fs.translator.properties_out: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:20 [INFO] idaes.init.fs.translator.properties_out: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:20 [INFO] idaes.init.fs.translator: Initialization Complete optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:20 [INFO] idaes.init.fs.C101.control_volume: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:20 [INFO] idaes.init.fs.C101: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:20 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:20 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:20 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:20 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:20 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:20 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:20 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:20 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:20 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:20 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:20 [INFO] idaes.init.fs.H102.control_volume.properties_out: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:20 [INFO] idaes.init.fs.H102.control_volume: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:20 [INFO] idaes.init.fs.H102.control_volume.properties_in: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:20 [INFO] idaes.init.fs.H102: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:21 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:21 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:21 [INFO] idaes.init.fs.H101.control_volume: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:21 [INFO] idaes.init.fs.H101: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:21 [INFO] idaes.init.fs.R101.control_volume: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:21 [INFO] idaes.init.fs.R101: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:21 [INFO] idaes.init.fs.F101.control_volume: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:21 [INFO] idaes.init.fs.F101: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:21 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:21 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:21 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:21 [INFO] idaes.init.fs.C101.control_volume: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:21 [INFO] idaes.init.fs.C101: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:21 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:21 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:22 [INFO] idaes.init.fs.H101.control_volume: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:22 [INFO] idaes.init.fs.H101: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:22 [INFO] idaes.init.fs.R101.control_volume: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:22 [INFO] idaes.init.fs.R101: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:22 [INFO] idaes.init.fs.F101.control_volume: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:22 [INFO] idaes.init.fs.F101: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:22 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:22 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:22 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:22 [INFO] idaes.init.fs.C101.control_volume: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:22 [INFO] idaes.init.fs.C101: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:22 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:22 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:22 [INFO] idaes.init.fs.H101.control_volume: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:23 [INFO] idaes.init.fs.H101: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:23 [INFO] idaes.init.fs.R101.control_volume: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:23 [INFO] idaes.init.fs.R101: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:23 [INFO] idaes.init.fs.F101.control_volume: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:23 [INFO] idaes.init.fs.F101: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:23 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:23 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:23 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:23 [INFO] idaes.init.fs.C101.control_volume: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:23 [INFO] idaes.init.fs.C101: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:23 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:23 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:23 [INFO] idaes.init.fs.H101.control_volume: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:23 [INFO] idaes.init.fs.H101: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:23 [INFO] idaes.init.fs.R101.control_volume: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:24 [INFO] idaes.init.fs.R101: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:24 [INFO] idaes.init.fs.F101.control_volume: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:24 [INFO] idaes.init.fs.F101: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:24 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:24 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:24 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:24 [INFO] idaes.init.fs.C101.control_volume: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:24 [INFO] idaes.init.fs.C101: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:24 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:24 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "WARNING: Wegstein failed to converge in 3 iterations\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:24 [INFO] idaes.init.fs.translator.properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:24 [INFO] idaes.init.fs.translator.properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:24 [INFO] idaes.init.fs.translator.properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:24 [INFO] idaes.init.fs.translator.properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:24 [INFO] idaes.init.fs.translator.properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:24 [INFO] idaes.init.fs.translator.properties_out: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:24 [INFO] idaes.init.fs.translator.properties_out: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:24 [INFO] idaes.init.fs.translator: Initialization Complete optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:24 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [INFO] idaes.init.fs.H102.control_volume.properties_out: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [INFO] idaes.init.fs.H102.control_volume: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [INFO] idaes.init.fs.H102.control_volume.properties_in: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [INFO] idaes.init.fs.H102: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- }
- ],
- "source": [
- "seq.run(m, function)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "We have now initialized the flowsheet. Let us run the flowsheet in a simulation mode to look at the results. \n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 39,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "WARNING: model contains export suffix\n",
- "'fs.H102.control_volume.properties_out[0.0].scaling_factor' that contains 1\n",
- "component keys that are not exported as part of the NL file. Skipping.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "WARNING: model contains export suffix 'fs.H102.control_volume.scaling_factor'\n",
- "that contains 1 component keys that are not exported as part of the NL file.\n",
- "Skipping.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "WARNING: model contains export suffix\n",
- "'fs.F101.control_volume.properties_out[0.0].scaling_factor' that contains 26\n",
- "component keys that are not exported as part of the NL file. Skipping.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "WARNING: model contains export suffix\n",
- "'fs.F101.control_volume.properties_in[0.0].scaling_factor' that contains 25\n",
- "component keys that are not exported as part of the NL file. Skipping.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "WARNING: model contains export suffix 'fs.F101.control_volume.scaling_factor'\n",
- "that contains 1 component keys that are not exported as part of the NL file.\n",
- "Skipping.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "WARNING: model contains export suffix\n",
- "'fs.R101.control_volume.properties_out[0.0].scaling_factor' that contains 25\n",
- "component keys that are not exported as part of the NL file. Skipping.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "WARNING: model contains export suffix\n",
- "'fs.R101.control_volume.properties_in[0.0].scaling_factor' that contains 25\n",
- "component keys that are not exported as part of the NL file. Skipping.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "WARNING: model contains export suffix 'fs.R101.control_volume.scaling_factor'\n",
- "that contains 2 component keys that are not exported as part of the NL file.\n",
- "Skipping.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "WARNING: model contains export suffix\n",
- "'fs.H101.control_volume.properties_out[0.0].scaling_factor' that contains 26\n",
- "component keys that are not exported as part of the NL file. Skipping.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "WARNING: model contains export suffix\n",
- "'fs.H101.control_volume.properties_in[0.0].scaling_factor' that contains 25\n",
- "component keys that are not exported as part of the NL file. Skipping.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Ipopt 3.13.2: nlp_scaling_method=gradient-based\n",
- "tol=1e-06\n",
- "max_iter=200\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "\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...: 1097\n",
- "Number of nonzeros in inequality constraint Jacobian.: 0\n",
- "Number of nonzeros in Lagrangian Hessian.............: 877\n",
- "\n",
- "Total number of variables............................: 363\n",
- " variables with only lower bounds: 8\n",
- " variables with lower and upper bounds: 155\n",
- " variables with only upper bounds: 0\n",
- "Total number of equality constraints.................: 363\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.82e+04 1.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
- " 1 0.0000000e+00 8.69e+03 1.44e+03 -1.0 2.00e+04 - 9.71e-01 4.67e-01H 1\n",
- " 2 0.0000000e+00 1.29e+03 1.56e+03 -1.0 1.60e+04 - 9.79e-01 4.90e-01h 1\n",
- " 3 0.0000000e+00 1.18e+03 1.55e+05 -1.0 1.40e+04 - 9.90e-01 4.99e-01h 1\n",
- " 4 0.0000000e+00 5.46e+02 2.32e+09 -1.0 8.43e+03 - 1.00e+00 9.82e-01h 1\n",
- " 5 0.0000000e+00 5.46e+03 3.66e+10 -1.0 5.97e+02 - 1.00e+00 9.90e-01h 1\n",
- " 6 0.0000000e+00 1.21e+03 8.01e+09 -1.0 5.75e+00 - 1.00e+00 1.00e+00h 1\n",
- " 7 0.0000000e+00 6.42e+00 3.87e+07 -1.0 1.53e-03 - 1.00e+00 1.00e+00f 1\n",
- " 8 0.0000000e+00 1.96e-04 9.36e+02 -1.0 7.28e-06 - 1.00e+00 1.00e+00h 1\n",
- " 9 0.0000000e+00 2.24e-08 5.91e-02 -3.8 1.38e-07 - 1.00e+00 1.00e+00h 1\n",
- "Cannot recompute multipliers for feasibility problem. Error in eq_mult_calculator\n",
- "\n",
- "Number of Iterations....: 9\n",
- "\n",
- " (scaled) (unscaled)\n",
- "Objective...............: 0.0000000000000000e+00 0.0000000000000000e+00\n",
- "Dual infeasibility......: 1.5042546731871284e+04 1.5042546731871284e+04\n",
- "Constraint violation....: 2.9103830456733704e-11 2.2351741790771484e-08\n",
- "Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
- "Overall NLP error.......: 2.9103830456733704e-11 1.5042546731871284e+04\n",
- "\n",
- "\n",
- "Number of objective function evaluations = 11\n",
- "Number of objective gradient evaluations = 10\n",
- "Number of equality constraint evaluations = 11\n",
- "Number of inequality constraint evaluations = 0\n",
- "Number of equality constraint Jacobian evaluations = 10\n",
- "Number of inequality constraint Jacobian evaluations = 0\n",
- "Number of Lagrangian Hessian evaluations = 9\n",
- "Total CPU secs in IPOPT (w/o function evaluations) = 0.010\n",
- "Total CPU secs in NLP function evaluations = 0.000\n",
- "\n",
- "EXIT: Optimal Solution Found.\n"
- ]
- }
- ],
- "source": [
- "# Create the solver object\n",
- "solver = get_solver()\n",
- "\n",
- "# Solve the model\n",
- "results = solver.solve(m, tee=True)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Add distillation column \n",
- "\n",
- "As mentioned earlier, the `SequentialDecomposition` tool currently does not support the distillation column model. Thus, we have not included the distillation column in the flowsheet. Now that we have a converged flowsheet, we will add the distillation column and simulate the entire flowsheet. \n",
- "\n",
- "In the following, we will\n",
- "- Add the distillation column \n",
- "- Connect it to the heater \n",
- "- Add the necessary equality constraints\n",
- "- Propogate the state variable information from the outlet of the heater to the inlet of the distillation column \n",
- "- Fix the degrees of freedom of the distillation block (reflux ratio, boilup ratio, and condenser pressure)\n",
- "- Scale the control volume heat variables to help convergence\n",
- "- Initialize the distillation block.\n",
- "\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 40,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:25 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].material_flow_terms[Liq,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].material_flow_terms[Vap,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].material_flow_terms[Liq,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].material_flow_terms[Vap,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].enthalpy_flow_terms[Liq], enthalpy_flow_terms\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].enthalpy_flow_terms[Vap], enthalpy_flow_terms\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].flow_mol_phase\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_comp[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_comp[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_phase_equilibrium[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_phase_equilibrium[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_P_vap[benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_P_vap[toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].material_flow_terms[Liq,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].material_flow_terms[Vap,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].material_flow_terms[Liq,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].material_flow_terms[Vap,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].enthalpy_flow_terms[Liq], enthalpy_flow_terms\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].enthalpy_flow_terms[Vap], enthalpy_flow_terms\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_flow_liq_out[0.0]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_mole_frac_liq_out[0.0,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_mole_frac_liq_out[0.0,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_flow_liq_out[0.0]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_mole_frac_liq_out[0.0,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_mole_frac_liq_out[0.0,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_flow_liq_out[0.0]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_mole_frac_liq_out[0.0,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_mole_frac_liq_out[0.0,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_flow_vap_out[0.0]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_mole_frac_vap_out[0.0,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_mole_frac_vap_out[0.0,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_flow_vap_out[0.0]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_mole_frac_vap_out[0.0,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_mole_frac_vap_out[0.0,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_flow_vap_out[0.0]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_mole_frac_vap_out[0.0,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_mole_frac_vap_out[0.0,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_flow_liq_out[0.0]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_mole_frac_liq_out[0.0,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_mole_frac_liq_out[0.0,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_flow_liq_out[0.0]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_mole_frac_liq_out[0.0,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_mole_frac_liq_out[0.0,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_flow_liq_out[0.0]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_mole_frac_liq_out[0.0,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_mole_frac_liq_out[0.0,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_flow_liq_out[0.0]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_mole_frac_liq_out[0.0,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_mole_frac_liq_out[0.0,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_flow_vap_out[0.0]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_mole_frac_vap_out[0.0,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_mole_frac_vap_out[0.0,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_flow_vap_out[0.0]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_mole_frac_vap_out[0.0,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_mole_frac_vap_out[0.0,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_flow_vap_out[0.0]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_mole_frac_vap_out[0.0,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_mole_frac_vap_out[0.0,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_flow_vap_out[0.0]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_mole_frac_vap_out[0.0,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_mole_frac_vap_out[0.0,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_flow_liq_out[0.0]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_mole_frac_liq_out[0.0,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_mole_frac_liq_out[0.0,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_flow_liq_out[0.0]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_mole_frac_liq_out[0.0,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_mole_frac_liq_out[0.0,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_flow_vap_out[0.0]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_mole_frac_vap_out[0.0,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_mole_frac_vap_out[0.0,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_flow_vap_out[0.0]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_mole_frac_vap_out[0.0,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_mole_frac_vap_out[0.0,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_flow_vap_out[0.0]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_mole_frac_vap_out[0.0,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_mole_frac_vap_out[0.0,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.e_flow_reflux[0.0]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.e_mole_frac_reflux[0.0,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.e_mole_frac_reflux[0.0,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_flow_liq_out[0.0]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_mole_frac_liq_out[0.0,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_mole_frac_liq_out[0.0,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.e_flow_vapor_reboil[0.0]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.e_mole_frac_vapor_reboil[0.0,benzene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.e_mole_frac_vapor_reboil[0.0,toluene]\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [INFO] idaes.init.fs.D101: Begin initialization.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [INFO] idaes.init.fs.D101.feed_tray: Begin initialization.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [INFO] idaes.init.fs.D101.feed_tray.properties_out: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:26 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:27 [INFO] idaes.init.fs.D101.feed_tray: Mass balance solve optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:27 [INFO] idaes.init.fs.D101.feed_tray: Mass and energy balance solve optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:27 [INFO] idaes.init.fs.D101.feed_tray: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:27 [INFO] idaes.init.fs.D101.feed_tray: Initialization complete, status optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:27 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:27 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:27 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:27 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:27 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:27 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:27 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:27 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:27 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:27 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:27 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:27 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:27 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:27 [INFO] idaes.init.fs.D101.condenser.control_volume: Initialization Complete\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:27 [INFO] idaes.init.fs.D101.condenser: Initialization Complete, optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:27 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:27 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:27 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:27 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:27 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:27 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:28 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:28 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:28 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:28 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:28 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:28 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:28 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:28 [INFO] idaes.init.fs.D101.reboiler: Initialization Complete, optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:28 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:28 [INFO] idaes.init.fs.D101.rectification_section[1]: Begin initialization.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:28 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:28 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:28 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:28 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:28 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:28 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:28 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:28 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:28 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:28 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:28 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:28 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:28 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:28 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:28 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:28 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:28 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[1]: Mass balance solve optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[1]: Mass and energy balance solve optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[1]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[1]: Initialization complete, status optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[2]: Begin initialization.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[2]: Mass balance solve optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[2]: Mass and energy balance solve optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[2]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[2]: Initialization complete, status optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[3]: Begin initialization.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:29 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.rectification_section[3]: Mass balance solve optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.rectification_section[3]: Mass and energy balance solve optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.rectification_section[3]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.rectification_section[3]: Initialization complete, status optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.rectification_section[4]: Begin initialization.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.rectification_section[4]: Mass balance solve optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.rectification_section[4]: Mass and energy balance solve optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.rectification_section[4]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.rectification_section[4]: Initialization complete, status optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.stripping_section[6]: Begin initialization.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:30 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[6]: Mass balance solve optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[6]: Mass and energy balance solve optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[6]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[6]: Initialization complete, status optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[7]: Begin initialization.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[7]: Mass balance solve optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[7]: Mass and energy balance solve optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[7]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[7]: Initialization complete, status optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[8]: Begin initialization.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:31 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[8]: Mass balance solve optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[8]: Mass and energy balance solve optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[8]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[8]: Initialization complete, status optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[9]: Begin initialization.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[9]: Mass balance solve optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[9]: Mass and energy balance solve optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[9]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[9]: Initialization complete, status optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[10]: Begin initialization.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:32 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:33 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:33 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:33 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:33 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:33 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:33 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:33 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:33 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:33 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:33 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:33 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:33 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:33 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Complete: optimal - Optimal Solution Found\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:33 [INFO] idaes.init.fs.D101.stripping_section[10]: Mass balance solve optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:33 [INFO] idaes.init.fs.D101.stripping_section[10]: Mass and energy balance solve optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:33 [INFO] idaes.init.fs.D101.stripping_section[10]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:33 [INFO] idaes.init.fs.D101.stripping_section[10]: Initialization complete, status optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:33 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:33 [INFO] idaes.init.fs.D101: Rectification section initialization status optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:33 [INFO] idaes.init.fs.D101: Stripping section initialization status optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:33 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:33 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:33 [INFO] idaes.init.fs.D101: Column section initialization status optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:33 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:33 [INFO] idaes.init.fs.D101: Column section + Condenser initialization status optimal - Optimal Solution Found.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:33 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: State Released.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-11-02 10:27:33 [INFO] idaes.init.fs.D101: Column section + Condenser + Reboiler initialization status optimal - Optimal Solution Found.\n"
+ "WARNING: model contains export suffix 'scaling_factor' that contains 6\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 150 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "Ipopt 3.13.2: nlp_scaling_method=gradient-based\n",
+ "tol=1e-06\n",
+ "max_iter=200\n",
+ "\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...: 1097\n",
+ "Number of nonzeros in inequality constraint Jacobian.: 0\n",
+ "Number of nonzeros in Lagrangian Hessian.............: 877\n",
+ "\n",
+ "Total number of variables............................: 363\n",
+ " variables with only lower bounds: 8\n",
+ " variables with lower and upper bounds: 155\n",
+ " variables with only upper bounds: 0\n",
+ "Total number of equality constraints.................: 363\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.82e+04 1.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
+ " 1 0.0000000e+00 8.69e+03 1.44e+03 -1.0 2.00e+04 - 9.71e-01 4.67e-01H 1\n",
+ " 2 0.0000000e+00 1.29e+03 1.56e+03 -1.0 1.60e+04 - 9.79e-01 4.90e-01h 1\n",
+ " 3 0.0000000e+00 1.18e+03 1.55e+05 -1.0 1.40e+04 - 9.90e-01 4.99e-01h 1\n",
+ " 4 0.0000000e+00 5.46e+02 2.32e+09 -1.0 8.42e+03 - 1.00e+00 9.82e-01h 1\n",
+ " 5 0.0000000e+00 5.46e+03 3.66e+10 -1.0 5.97e+02 - 1.00e+00 9.90e-01h 1\n",
+ " 6 0.0000000e+00 1.21e+03 8.01e+09 -1.0 5.75e+00 - 1.00e+00 1.00e+00h 1\n",
+ " 7 0.0000000e+00 6.41e+00 3.87e+07 -1.0 1.53e-03 - 1.00e+00 1.00e+00f 1\n",
+ " 8 0.0000000e+00 1.96e-04 9.36e+02 -1.0 7.28e-06 - 1.00e+00 1.00e+00h 1\n",
+ " 9 0.0000000e+00 2.24e-08 4.99e-01 -3.8 5.92e-08 - 1.00e+00 1.00e+00h 1\n",
+ "Cannot recompute multipliers for feasibility problem. Error in eq_mult_calculator\n",
+ "\n",
+ "Number of Iterations....: 9\n",
+ "\n",
+ " (scaled) (unscaled)\n",
+ "Objective...............: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Dual infeasibility......: 1.5042487592972509e+04 1.5042487592972509e+04\n",
+ "Constraint violation....: 2.9103830456733704e-11 2.2351741790771484e-08\n",
+ "Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Overall NLP error.......: 2.9103830456733704e-11 1.5042487592972509e+04\n",
+ "\n",
+ "\n",
+ "Number of objective function evaluations = 11\n",
+ "Number of objective gradient evaluations = 10\n",
+ "Number of equality constraint evaluations = 11\n",
+ "Number of inequality constraint evaluations = 0\n",
+ "Number of equality constraint Jacobian evaluations = 10\n",
+ "Number of inequality constraint Jacobian evaluations = 0\n",
+ "Number of Lagrangian Hessian evaluations = 9\n",
+ "Total CPU secs in IPOPT (w/o function evaluations) = 0.011\n",
+ "Total CPU secs in NLP function evaluations = 0.001\n",
+ "\n",
+ "EXIT: Optimal Solution Found.\n"
]
- },
+ }
+ ],
+ "source": [
+ "# Create the solver object\n",
+ "solver = get_solver()\n",
+ "\n",
+ "# Solve the model\n",
+ "results = solver.solve(m, tee=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Add distillation column \n",
+ "\n",
+ "As mentioned earlier, the `SequentialDecomposition` tool currently does not support the distillation column model. Thus, we have not included the distillation column in the flowsheet. Now that we have a converged flowsheet, we will add the distillation column and simulate the entire flowsheet. \n",
+ "\n",
+ "In the following, we will\n",
+ "- Add the distillation column \n",
+ "- Connect it to the heater \n",
+ "- Add the necessary equality constraints\n",
+ "- Propagate the state variable information from the outlet of the heater to the inlet of the distillation column \n",
+ "- Fix the degrees of freedom of the distillation block (reflux ratio, boilup ratio, and condenser pressure)\n",
+ "- Scale the control volume heat variables to help convergence\n",
+ "- Initialize the distillation block.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 50,
+ "metadata": {},
+ "outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "2023-11-02 10:27:33 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: State Released.\n"
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].material_flow_terms[Liq,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].material_flow_terms[Vap,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].material_flow_terms[Liq,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].material_flow_terms[Vap,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].enthalpy_flow_terms[Liq], enthalpy_flow_terms\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].enthalpy_flow_terms[Vap], enthalpy_flow_terms\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].material_flow_terms[Liq,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].material_flow_terms[Vap,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].material_flow_terms[Liq,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].material_flow_terms[Vap,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].enthalpy_flow_terms[Liq], enthalpy_flow_terms\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].enthalpy_flow_terms[Vap], enthalpy_flow_terms\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.e_flow_reflux[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.e_mole_frac_reflux[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.e_mole_frac_reflux[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.e_flow_vapor_reboil[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.e_mole_frac_vapor_reboil[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.e_mole_frac_vapor_reboil[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101: Begin initialization.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray: Begin initialization.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: State Released.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: State Released.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser.control_volume: Initialization Complete\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser: Initialization Complete, optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: State Released.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: State Released.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler: Initialization Complete, optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: State Released.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.rectification_section[1]: Begin initialization.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: State Released.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[2]: Begin initialization.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: State Released.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[2]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[2]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3]: Begin initialization.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: State Released.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[3]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[3]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[3]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4]: Begin initialization.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: State Released.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.stripping_section[6]: Begin initialization.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: State Released.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[7]: Begin initialization.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: State Released.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[7]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[7]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8]: Begin initialization.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: State Released.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[8]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[8]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[8]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9]: Begin initialization.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: State Released.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10]: Begin initialization.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: State Released.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: State Released.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101: Rectification section initialization status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101: Stripping section initialization status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: State Released.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: State Released.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101: Column section initialization status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: State Released.\n",
+ "2024-08-28 18:39:02 [INFO] idaes.init.fs.D101: Column section + Condenser initialization status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:02 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: State Released.\n",
+ "2024-08-28 18:39:03 [INFO] idaes.init.fs.D101: Column section + Condenser + Reboiler initialization status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:03 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: State Released.\n"
]
}
],
@@ -6528,7 +2024,7 @@
},
{
"cell_type": "code",
- "execution_count": 41,
+ "execution_count": 51,
"metadata": {},
"outputs": [],
"source": [
@@ -6563,113 +2059,17 @@
},
{
"cell_type": "code",
- "execution_count": 42,
+ "execution_count": 53,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "WARNING: model contains export suffix\n",
- "'fs.D101.condenser.control_volume.properties_out[0.0].scaling_factor' that\n",
- "contains 1 component keys that are not exported as part of the NL file.\n",
- "Skipping.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "WARNING: model contains export suffix\n",
- "'fs.H102.control_volume.properties_out[0.0].scaling_factor' that contains 1\n",
- "component keys that are not exported as part of the NL file. Skipping.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "WARNING: model contains export suffix 'fs.H102.control_volume.scaling_factor'\n",
- "that contains 1 component keys that are not exported as part of the NL file.\n",
- "Skipping.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "WARNING: model contains export suffix\n",
- "'fs.F101.control_volume.properties_out[0.0].scaling_factor' that contains 26\n",
- "component keys that are not exported as part of the NL file. Skipping.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "WARNING: model contains export suffix\n",
- "'fs.F101.control_volume.properties_in[0.0].scaling_factor' that contains 25\n",
- "component keys that are not exported as part of the NL file. Skipping.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "WARNING: model contains export suffix 'fs.F101.control_volume.scaling_factor'\n",
- "that contains 1 component keys that are not exported as part of the NL file.\n",
- "Skipping.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "WARNING: model contains export suffix\n",
- "'fs.R101.control_volume.properties_out[0.0].scaling_factor' that contains 25\n",
- "component keys that are not exported as part of the NL file. Skipping.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "WARNING: model contains export suffix\n",
- "'fs.R101.control_volume.properties_in[0.0].scaling_factor' that contains 25\n",
- "component keys that are not exported as part of the NL file. Skipping.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "WARNING: model contains export suffix 'fs.R101.control_volume.scaling_factor'\n",
- "that contains 2 component keys that are not exported as part of the NL file.\n",
- "Skipping.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "WARNING: model contains export suffix\n",
- "'fs.H101.control_volume.properties_out[0.0].scaling_factor' that contains 26\n",
- "component keys that are not exported as part of the NL file. Skipping.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "WARNING: model contains export suffix\n",
- "'fs.H101.control_volume.properties_in[0.0].scaling_factor' that contains 25\n",
- "component keys that are not exported as part of the NL file. Skipping.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
+ "WARNING: model contains export suffix 'scaling_factor' that contains 7\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 150 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
"Ipopt 3.13.2: nlp_scaling_method=gradient-based\n",
"tol=1e-06\n",
"max_iter=200\n",
@@ -6719,30 +2119,28 @@
" 6 0.0000000e+00 1.16e+03 7.80e+09 -1.0 5.36e+00 - 1.00e+00 1.00e+00h 1\n",
" 7 0.0000000e+00 5.96e+00 3.64e+07 -1.0 1.47e-03 - 1.00e+00 1.00e+00f 1\n",
" 8 0.0000000e+00 1.69e-04 8.15e+02 -1.0 6.77e-06 - 1.00e+00 1.00e+00h 1\n",
- " 9 0.0000000e+00 1.27e-04 7.49e+06 -3.8 5.68e-08 - 1.00e+00 2.50e-01h 3\n",
- "iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n",
- " 10 0.0000000e+00 9.51e-05 5.62e+06 -3.8 1.19e-06 - 1.00e+00 2.50e-01h 3\n",
+ " 9 0.0000000e+00 7.45e-09 6.64e-03 -3.8 2.00e-07 - 1.00e+00 1.00e+00h 1\n",
"Cannot recompute multipliers for feasibility problem. Error in eq_mult_calculator\n",
"\n",
- "Number of Iterations....: 10\n",
+ "Number of Iterations....: 9\n",
"\n",
" (scaled) (unscaled)\n",
"Objective...............: 0.0000000000000000e+00 0.0000000000000000e+00\n",
- "Dual infeasibility......: 5.6320546602153890e+06 5.6320546602153890e+06\n",
- "Constraint violation....: 2.9103830456733704e-11 9.5061208412516862e-05\n",
+ "Dual infeasibility......: 1.5042483516409773e+04 1.5042483516409773e+04\n",
+ "Constraint violation....: 2.9103830456733704e-11 7.4505805969238281e-09\n",
"Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
- "Overall NLP error.......: 2.9103830456733704e-11 5.6320546602153890e+06\n",
+ "Overall NLP error.......: 2.9103830456733704e-11 1.5042483516409773e+04\n",
"\n",
"\n",
- "Number of objective function evaluations = 18\n",
- "Number of objective gradient evaluations = 11\n",
- "Number of equality constraint evaluations = 18\n",
+ "Number of objective function evaluations = 11\n",
+ "Number of objective gradient evaluations = 10\n",
+ "Number of equality constraint evaluations = 11\n",
"Number of inequality constraint evaluations = 0\n",
- "Number of equality constraint Jacobian evaluations = 11\n",
+ "Number of equality constraint Jacobian evaluations = 10\n",
"Number of inequality constraint Jacobian evaluations = 0\n",
- "Number of Lagrangian Hessian evaluations = 10\n",
- "Total CPU secs in IPOPT (w/o function evaluations) = 0.011\n",
- "Total CPU secs in NLP function evaluations = 0.010\n",
+ "Number of Lagrangian Hessian evaluations = 9\n",
+ "Total CPU secs in IPOPT (w/o function evaluations) = 0.083\n",
+ "Total CPU secs in NLP function evaluations = 0.013\n",
"\n",
"EXIT: Optimal Solution Found.\n"
]
@@ -6750,10 +2148,10 @@
{
"data": {
"text/plain": [
- "{'Problem': [{'Lower bound': -inf, 'Upper bound': inf, 'Number of objectives': 1, 'Number of constraints': 1169, 'Number of variables': 1169, 'Sense': 'unknown'}], 'Solver': [{'Status': 'ok', 'Message': 'Ipopt 3.13.2\\\\x3a Optimal Solution Found', 'Termination condition': 'optimal', 'Id': 0, 'Error rc': 0, 'Time': 0.08404064178466797}], 'Solution': [OrderedDict([('number of solutions', 0), ('number of solutions displayed', 0)])]}"
+ "{'Problem': [{'Lower bound': -inf, 'Upper bound': inf, 'Number of objectives': 1, 'Number of constraints': 1169, 'Number of variables': 1169, 'Sense': 'unknown'}], 'Solver': [{'Status': 'ok', 'Message': 'Ipopt 3.13.2\\\\x3a Optimal Solution Found', 'Termination condition': 'optimal', 'Id': 0, 'Error rc': 0, 'Time': 0.2022566795349121}], 'Solution': [OrderedDict([('number of solutions', 0), ('number of solutions displayed', 0)])]}"
]
},
- "execution_count": 42,
+ "execution_count": 53,
"metadata": {},
"output_type": "execute_result"
}
@@ -6773,25 +2171,25 @@
},
{
"cell_type": "code",
- "execution_count": 43,
+ "execution_count": 55,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "total cost = $ 442301.4707525308\n",
- "operating cost = $ 427596.7305680824\n",
- "capital cost = $ 14704.74018444835\n",
+ "total cost = $ 442301.47075252194\n",
+ "operating cost = $ 427596.73056805483\n",
+ "capital cost = $ 14704.740184467111\n",
"\n",
- "Distillate flowrate = 0.16196898920642744 mol/s\n",
- "Benzene purity = 89.49161665800828 %\n",
- "Residue flowrate = 0.1051500712068829 mol/s\n",
- "Toluene purity = 43.32260291055269 %\n",
+ "Distillate flowrate = 0.16196898920633368 mol/s\n",
+ "Benzene purity = 89.4916166580088 %\n",
+ "Residue flowrate = 0.10515007120697904 mol/s\n",
+ "Toluene purity = 43.32260291055251 %\n",
"\n",
"Conversion = 75.0 %\n",
"\n",
- "Overhead benzene loss in F101 = 42.16193848360187 %\n"
+ "Overhead benzene loss in F101 = 42.161938483603194 %\n"
]
}
],
@@ -6837,7 +2235,7 @@
},
{
"cell_type": "code",
- "execution_count": 44,
+ "execution_count": 57,
"metadata": {},
"outputs": [
{
@@ -6886,7 +2284,7 @@
},
{
"cell_type": "code",
- "execution_count": 45,
+ "execution_count": 58,
"metadata": {},
"outputs": [
{
@@ -6940,7 +2338,7 @@
},
{
"cell_type": "code",
- "execution_count": 46,
+ "execution_count": 59,
"metadata": {},
"outputs": [
{
@@ -6977,7 +2375,7 @@
"source": [
"\n",
"Inline Exercise:\n",
- "You can querry additional variables here if you like. \n",
+ "You can query additional variables here if you like. \n",
"\n",
"Use Shift+Enter to run the cell once you have typed in your code. \n",
"
"
@@ -7016,7 +2414,7 @@
},
{
"cell_type": "code",
- "execution_count": 47,
+ "execution_count": 60,
"metadata": {},
"outputs": [],
"source": [
@@ -7032,7 +2430,7 @@
},
{
"cell_type": "code",
- "execution_count": 48,
+ "execution_count": 61,
"metadata": {},
"outputs": [],
"source": [
@@ -7059,7 +2457,7 @@
},
{
"cell_type": "code",
- "execution_count": 49,
+ "execution_count": 63,
"metadata": {
"tags": [
"solution"
@@ -7088,7 +2486,7 @@
},
{
"cell_type": "code",
- "execution_count": 50,
+ "execution_count": 64,
"metadata": {},
"outputs": [],
"source": [
@@ -7130,7 +2528,7 @@
},
{
"cell_type": "code",
- "execution_count": 51,
+ "execution_count": 66,
"metadata": {
"tags": [
"solution"
@@ -7156,7 +2554,7 @@
},
{
"cell_type": "code",
- "execution_count": 52,
+ "execution_count": 67,
"metadata": {},
"outputs": [],
"source": [
@@ -7181,7 +2579,7 @@
},
{
"cell_type": "code",
- "execution_count": 53,
+ "execution_count": 69,
"metadata": {
"tags": [
"solution"
@@ -7202,7 +2600,7 @@
},
{
"cell_type": "code",
- "execution_count": 54,
+ "execution_count": 70,
"metadata": {},
"outputs": [],
"source": [
@@ -7223,103 +2621,20 @@
},
{
"cell_type": "code",
- "execution_count": 55,
+ "execution_count": 71,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "WARNING: model contains export suffix 'fs.H102.control_volume.scaling_factor'\n",
- "that contains 1 component keys that are not exported as part of the NL file.\n",
- "Skipping.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "WARNING: model contains export suffix\n",
- "'fs.F101.control_volume.properties_out[0.0].scaling_factor' that contains 25\n",
- "component keys that are not exported as part of the NL file. Skipping.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "WARNING: model contains export suffix\n",
- "'fs.F101.control_volume.properties_in[0.0].scaling_factor' that contains 25\n",
- "component keys that are not exported as part of the NL file. Skipping.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "WARNING: model contains export suffix 'fs.F101.control_volume.scaling_factor'\n",
- "that contains 1 component keys that are not exported as part of the NL file.\n",
- "Skipping.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "WARNING: model contains export suffix\n",
- "'fs.R101.control_volume.properties_out[0.0].scaling_factor' that contains 25\n",
- "component keys that are not exported as part of the NL file. Skipping.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "WARNING: model contains export suffix\n",
- "'fs.R101.control_volume.properties_in[0.0].scaling_factor' that contains 25\n",
- "component keys that are not exported as part of the NL file. Skipping.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "WARNING: model contains export suffix 'fs.R101.control_volume.scaling_factor'\n",
- "that contains 2 component keys that are not exported as part of the NL file.\n",
- "Skipping.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "WARNING: model contains export suffix\n",
- "'fs.H101.control_volume.properties_out[0.0].scaling_factor' that contains 25\n",
- "component keys that are not exported as part of the NL file. Skipping.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "WARNING: model contains export suffix\n",
- "'fs.H101.control_volume.properties_in[0.0].scaling_factor' that contains 25\n",
- "component keys that are not exported as part of the NL file. Skipping.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
+ "WARNING: model contains export suffix 'scaling_factor' that contains 3\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 150 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
"Ipopt 3.13.2: nlp_scaling_method=gradient-based\n",
"tol=1e-06\n",
- "max_iter=200\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
+ "max_iter=200\n",
"\n",
"\n",
"******************************************************************************\n",
@@ -7360,13 +2675,7 @@
" 0 4.4230147e+05 2.99e+05 9.90e+01 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
" 1 4.3753585e+05 2.91e+05 1.28e+02 -1.0 3.09e+06 - 3.58e-01 2.40e-02f 1\n",
" 2 4.3545100e+05 2.78e+05 1.55e+02 -1.0 1.78e+06 - 3.31e-01 4.74e-02h 1\n",
- " 3 4.2822311e+05 2.20e+05 4.50e+02 -1.0 2.99e+06 - 2.95e-02 1.35e-01h 1\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
+ " 3 4.2822311e+05 2.20e+05 4.50e+02 -1.0 2.99e+06 - 2.95e-02 1.35e-01h 1\n",
" 4 4.2249096e+05 1.45e+05 1.43e+03 -1.0 7.01e+06 - 5.14e-01 2.03e-01h 1\n",
" 5 4.2194364e+05 8.17e+04 1.70e+04 -1.0 6.06e+06 - 5.97e-01 4.28e-01h 1\n",
" 6 4.2602765e+05 4.55e+04 1.10e+06 -1.0 4.32e+06 - 9.26e-01 5.07e-01h 1\n",
@@ -7378,14 +2687,8 @@
" 11 4.5079086e+05 1.19e+03 1.54e+09 -1.0 5.63e+02 -4.5 3.77e-01 1.00e+00h 1\n",
" 12 4.5024224e+05 2.66e+00 3.67e+06 -1.0 6.61e+01 -5.0 1.00e+00 1.00e+00f 1\n",
" 13 4.4946170e+05 5.64e-01 9.29e+05 -1.0 1.81e+02 -5.4 1.00e+00 7.88e-01f 1\n",
- " 14 4.4916780e+05 8.48e+00 1.62e+05 -1.0 2.83e+02 -5.9 1.00e+00 1.00e+00f 1\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- " 15 4.4899127e+05 4.83e+00 9.06e+04 -1.0 1.01e+02 -6.4 1.00e+00 4.40e-01f 2\n",
+ " 14 4.4916780e+05 8.48e+00 1.62e+05 -1.0 2.83e+02 -5.9 1.00e+00 1.00e+00f 1\n",
+ " 15 4.4899127e+05 4.83e+00 9.07e+04 -1.0 1.01e+02 -6.4 1.00e+00 4.40e-01f 2\n",
" 16 4.4886718e+05 7.00e-01 4.61e+02 -1.0 2.35e+02 -6.9 1.00e+00 1.00e+00f 1\n",
" 17 4.4800159e+05 1.39e+02 4.52e+06 -3.8 1.17e+03 -7.3 9.79e-01 9.37e-01f 1\n",
" 18 4.4672196e+05 9.59e+02 1.22e+06 -3.8 4.55e+03 -7.8 1.00e+00 9.43e-01f 1\n",
@@ -7400,21 +2703,21 @@
" 26 4.3883308e+05 1.49e+03 8.46e+04 -3.8 1.02e+06 - 1.00e+00 1.35e-01h 1\n",
" 27 4.3885472e+05 2.18e+01 3.40e+03 -3.8 1.38e+05 - 1.00e+00 1.00e+00h 1\n",
" 28 4.3884160e+05 5.90e-02 6.38e+01 -3.8 8.66e+03 - 1.00e+00 1.00e+00h 1\n",
- " 29 4.3884157e+05 6.41e-07 4.63e-04 -3.8 2.89e+01 - 1.00e+00 1.00e+00h 1\n",
+ " 29 4.3884157e+05 6.48e-07 4.63e-04 -3.8 2.89e+01 - 1.00e+00 1.00e+00h 1\n",
"iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n",
" 30 4.3883990e+05 3.57e-01 2.38e+03 -5.7 8.19e+02 - 1.00e+00 1.00e+00f 1\n",
- " 31 4.3883992e+05 3.65e-07 1.29e-05 -5.7 3.55e-01 - 1.00e+00 1.00e+00h 1\n",
- " 32 4.3883990e+05 5.46e-05 3.63e-01 -8.0 1.01e+01 - 1.00e+00 1.00e+00h 1\n",
- " 33 4.3883990e+05 3.73e-08 3.38e-07 -8.0 5.42e-05 - 1.00e+00 1.00e+00h 1\n",
+ " 31 4.3883992e+05 3.50e-07 7.79e-06 -5.7 3.55e-01 - 1.00e+00 1.00e+00h 1\n",
+ " 32 4.3883990e+05 5.47e-05 3.63e-01 -8.0 1.01e+01 - 1.00e+00 1.00e+00h 1\n",
+ " 33 4.3883990e+05 2.24e-08 1.46e-07 -8.0 5.42e-05 - 1.00e+00 1.00e+00h 1\n",
"\n",
"Number of Iterations....: 33\n",
"\n",
" (scaled) (unscaled)\n",
- "Objective...............: 4.3883989842628671e+02 4.3883989842628670e+05\n",
- "Dual infeasibility......: 3.3796318887468276e-07 3.3796318887468276e-04\n",
- "Constraint violation....: 2.9103830456733704e-11 3.7252902984619141e-08\n",
- "Complementarity.........: 9.0909948039808002e-09 9.0909948039808004e-06\n",
- "Overall NLP error.......: 9.0909948039808002e-09 3.3796318887468276e-04\n",
+ "Objective...............: 4.3883989842628603e+02 4.3883989842628600e+05\n",
+ "Dual infeasibility......: 1.4600704448671754e-07 1.4600704448671753e-04\n",
+ "Constraint violation....: 2.9103830456733704e-11 2.2351741790771484e-08\n",
+ "Complementarity.........: 9.0909948039799681e-09 9.0909948039799686e-06\n",
+ "Overall NLP error.......: 9.0909948039799681e-09 1.4600704448671753e-04\n",
"\n",
"\n",
"Number of objective function evaluations = 35\n",
@@ -7424,8 +2727,8 @@
"Number of equality constraint Jacobian evaluations = 34\n",
"Number of inequality constraint Jacobian evaluations = 34\n",
"Number of Lagrangian Hessian evaluations = 33\n",
- "Total CPU secs in IPOPT (w/o function evaluations) = 0.076\n",
- "Total CPU secs in NLP function evaluations = 0.017\n",
+ "Total CPU secs in IPOPT (w/o function evaluations) = 0.164\n",
+ "Total CPU secs in NLP function evaluations = 0.020\n",
"\n",
"EXIT: Optimal Solution Found.\n"
]
@@ -7446,25 +2749,25 @@
},
{
"cell_type": "code",
- "execution_count": 56,
+ "execution_count": 73,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "total cost = $ 438839.8984262867\n",
- "operating cost = $ 408883.53148308955\n",
- "capital cost = $ 29956.366943197143\n",
+ "total cost = $ 438839.898426286\n",
+ "operating cost = $ 408883.5314830889\n",
+ "capital cost = $ 29956.3669431971\n",
"\n",
- "Distillate flowrate = 0.17999999002639888 mol/s\n",
- "Benzene purity = 98.99999900049087 %\n",
- "Residue flowrate = 0.10851616424263709 mol/s\n",
- "Toluene purity = 15.676178086212413 %\n",
+ "Distillate flowrate = 0.1799999900263989 mol/s\n",
+ "Benzene purity = 98.99999900049086 %\n",
+ "Residue flowrate = 0.1085161642426372 mol/s\n",
+ "Toluene purity = 15.676178086213548 %\n",
"\n",
- "Conversion = 93.38705916369456 %\n",
+ "Conversion = 93.38705916369427 %\n",
"\n",
- "Overhead benzene loss in F101 = 17.340617931156157 %\n"
+ "Overhead benzene loss in F101 = 17.34061793115618 %\n"
]
}
],
@@ -7510,7 +2813,7 @@
},
{
"cell_type": "code",
- "execution_count": 57,
+ "execution_count": 75,
"metadata": {},
"outputs": [
{
@@ -7519,9 +2822,9 @@
"text": [
"Optimal Values\n",
"\n",
- "H101 outlet temperature = 568.9232042951961 K\n",
+ "H101 outlet temperature = 568.923204295196 K\n",
"\n",
- "R101 outlet temperature = 790.3655425698863 K\n",
+ "R101 outlet temperature = 790.3655425698853 K\n",
"\n",
"F101 outlet temperature = 298.0 K\n",
"\n",
@@ -7558,13 +2861,6 @@
"\n",
"Finally, observe that the operating temperature of the flash (F101) is almost at its lower bound. This helps in minimizing the amount of benzene in the vapor stream leaving the flash."
]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
}
],
"metadata": {
@@ -7584,7 +2880,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.11.5"
+ "version": "3.10.13"
}
},
"nbformat": 4,
diff --git a/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_distillation_exercise.ipynb b/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_distillation_exercise.ipynb
index 1a4834ff..e44357de 100644
--- a/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_distillation_exercise.ipynb
+++ b/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_distillation_exercise.ipynb
@@ -1,1632 +1,2857 @@
{
- "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": [
- "\n",
- "# HDA Flowsheet Simulation and Optimization\n",
- "Maintainer: Brandon Paul \n",
- "Author: Brandon Paul \n",
- "Updated: 2023-06-01 \n",
- "\n",
- "\n",
- "## Note\n",
- "\n",
- "This tutorial will be similar to the HDA flowsheet tutorial in the Tutorials section, except that we use a distillation column instead of a second flash (F102) to produce benzene and toluene products.\n",
- "\n",
- "\n",
- "## Learning outcomes\n",
- "\n",
- "\n",
- "- Construct a steady-state flowsheet using the IDAES unit model library\n",
- "- Connecting unit models in a flowsheet using Arcs\n",
- "- Using the SequentialDecomposition tool to initialize a flowsheet with recycle\n",
- "- Fomulate and solve an optimization problem\n",
- " - Defining an objective function\n",
- " - Setting variable bounds\n",
- " - Adding additional constraints \n",
- "\n",
- "\n",
- "## Problem Statement\n",
- "\n",
- "Hydrodealkylation is a chemical reaction that often involves reacting\n",
- "an aromatic hydrocarbon in the presence of hydrogen gas to form a\n",
- "simpler aromatic hydrocarbon devoid of functional groups. In this\n",
- "example, toluene will be reacted with hydrogen gas at high temperatures\n",
- " to form benzene via the following reaction:\n",
- "\n",
- "**C6H5CH3 + H2 \u2192 C6H6 + CH4**\n",
- "\n",
- "\n",
- "This reaction is often accompanied by an equilibrium side reaction\n",
- "which forms diphenyl, which we will neglect for this example.\n",
- "\n",
- "This example is based on the 1967 AIChE Student Contest problem as\n",
- "present by Douglas, J.M., Chemical Design of Chemical Processes, 1988,\n",
- "McGraw-Hill.\n",
- "\n",
- "The flowsheet that we will be using for this module is shown below with the stream conditions. We will be processing toluene and hydrogen to produce at least 370 TPY of benzene. As shown in the flowsheet, we use a flash tank, F101, to separate out the non-condensibles, and a distillation column, D101, to further separate the benzene-toluene mixture to improve the benzene purity. The non-condensibles separated out in F101 will be partially recycled back to M101 and the rest will be purged. We will assume ideal gas behavior for this flowsheet. The properties required for this module are defined in\n",
- "\n",
- "- `hda_ideal_VLE.py`\n",
- "- `idaes.models.properties.activity_coeff_models.BTX_activity_coeff_VLE`\n",
- "- `hda_reaction.py`\n",
- "\n",
- "We will be using two thermodynamic packages: one (first in the list above) containing all four components (i.e., toluene, hydrogen, benzene, and methane) and the other (second in the list above) containing benzene and toluene only. The latter is needed to simplify the VLE calculations in the distillation column model. \n",
- "\n",
- "![](HDA_flowsheet_distillation.png)\n",
- "\n",
- ""
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Translator block\n",
- "\n",
- "Benzene and toluene are separated by distillation, so the process involves phase equilibrium and two-phase flow conditions. However, the presence of hydrogen and methane complicates the calculations. This is because, hydrogen and methane are non-condensable under all conditions of interest; ergo, a vapor phase will always be present, and the mixture bubble point is extremely low. To simplify the phase equilibrium calculations, hydrogen and methane will be considered completely as non-condensable and insoluble in the liquid outlet from the flash F101.\n",
- "\n",
- "Since no hydrogen and methane will be present in the unit operations following the flash, a different component list can be used to simplify the property calculations. IDAES supports the definition of multiple property packages within a single flowsheet via `Translator` blocks. `Translator` blocks convert between different property calculations, component lists, and equations of state. "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Importing required pyomo and idaes components\n",
- "\n",
- "\n",
- "To construct a flowsheet, we will need several components from the pyomo and idaes package. Let us first import the following components from Pyomo:\n",
- "- Constraint (to write constraints)\n",
- "- Var (to declare variables)\n",
- "- ConcreteModel (to create the concrete model object)\n",
- "- Expression (to evaluate values as a function of variables defined in the model)\n",
- "- Objective (to define an objective function for optimization)\n",
- "- SolverFactory (to solve the problem)\n",
- "- TransformationFactory (to apply certain transformations)\n",
- "- Arc (to connect two unit models)\n",
- "- SequentialDecomposition (to initialize the flowsheet in a sequential mode)\n",
- "\n",
- "For further details on these components, please refer to the pyomo documentation: https://pyomo.readthedocs.io/en/stable/\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "from pyomo.environ import (\n",
- " Constraint,\n",
- " Var,\n",
- " ConcreteModel,\n",
- " Expression,\n",
- " Objective,\n",
- " TransformationFactory,\n",
- " value,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "Import `Arc` and `SequentialDecomposition` tools from `pyomo.network`\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Import the above mentioned tools from pyomo.network"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "From IDAES, we will be needing the FlowsheetBlock and the following unit models:\n",
- "- Mixer\n",
- "- Heater\n",
- "- CSTR\n",
- "- Flash\n",
- "- Separator (splitter) \n",
- "- PressureChanger\n",
- "- Translator (to switch from one property package to another)\n",
- "- TrayColumn (distillation column)\n",
- "- CondenserType (Type of the overhead condenser: complete or partial)\n",
- "- TemperatureSpec (Temperature specification inside the condenser)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [],
- "source": [
- "from idaes.core import FlowsheetBlock"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {},
- "outputs": [],
- "source": [
- "from idaes.models.unit_models import (\n",
- " PressureChanger,\n",
- " Mixer,\n",
- " Separator as Splitter,\n",
- " Heater,\n",
- " CSTR,\n",
- " Flash,\n",
- " Translator,\n",
- ")\n",
- "\n",
- "from idaes.models_extra.column_models import TrayColumn\n",
- "from idaes.models_extra.column_models.condenser import CondenserType, TemperatureSpec"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We will also be needing some utility tools to put together the flowsheet and calculate the degrees of freedom. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Utility tools to put together the flowsheet and calculate the degrees of freedom\n",
- "from idaes.models.unit_models.pressure_changer import ThermodynamicAssumption\n",
- "from idaes.core.util.model_statistics import degrees_of_freedom\n",
- "from idaes.core.util.initialization import propagate_state\n",
- "from idaes.core.solvers import get_solver\n",
- "import idaes.core.util.scaling as iscale\n",
- "\n",
- "# Import idaes logger to set output levels\n",
- "import idaes.logger as idaeslog"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Importing required thermo and reaction packages\n",
- "\n",
- "Finally, we import the thermophysical (`ideal_VLE.py` and `BTXParameterBlock`) packages and reaction package (`reaction.py`) for the HDA process. We have created custom thermophysical packages that assume ideal gas behavior with support for VLE. The reaction package consists of the stochiometric coefficients for the reaction, heat of reaction, and kinetic information (Arrhenius constant and activation energy). "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "metadata": {},
- "outputs": [],
- "source": [
- "from idaes_examples.mod.hda import hda_reaction as reaction_props\n",
- "from idaes.models.properties.activity_coeff_models.BTX_activity_coeff_VLE import (\n",
- " BTXParameterBlock,\n",
- ")\n",
- "\n",
- "from idaes_examples.mod.hda.hda_ideal_VLE import HDAParameterBlock"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Constructing the Flowsheet\n",
- "\n",
- "We have now imported all the components, unit models, and property modules we need to construct a flowsheet. Let us create a ConcreteModel and add the flowsheet block to it. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Create a Pyomo Concrete Model to contain the problem\n",
- "m = ConcreteModel()\n",
- "\n",
- "# Add a steady state flowsheet block to the model\n",
- "m.fs = FlowsheetBlock(dynamic=False)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We will now add the thermophysical and reaction packages to the flowsheet."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Property package for benzene, toluene, hydrogen, methane mixture\n",
- "m.fs.BTHM_params = HDAParameterBlock()\n",
- "\n",
- "# Property package for the benzene-toluene mixture\n",
- "m.fs.BT_params = BTXParameterBlock(\n",
- " valid_phase=(\"Liq\", \"Vap\"), activity_coeff_model=\"Ideal\"\n",
- ")\n",
- "\n",
- "# Reaction package for the HDA reaction\n",
- "m.fs.reaction_params = reaction_props.HDAReactionParameterBlock(\n",
- " property_package=m.fs.BTHM_params\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Adding Unit Models\n",
- "\n",
- "Let us start adding the unit models we have imported to the flowsheet. Here, we are adding the Mixer (assigned a name M101) and a Heater (assigned a name H101). Note that, all unit models need to be given a property package argument. In addition, the Mixer unit model needs a `list` consisting of the inlets (toluene feed, hydrogen feed and vapor recycle streams in this case). "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 11,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Adding the mixer M101 to the flowsheet\n",
- "m.fs.M101 = Mixer(\n",
- " property_package=m.fs.BTHM_params,\n",
- " inlet_list=[\"toluene_feed\", \"hydrogen_feed\", \"vapor_recycle\"],\n",
- ")\n",
- "\n",
- "# Adding the heater H101 to the flowsheet\n",
- "m.fs.H101 = Heater(property_package=m.fs.BTHM_params, has_phase_equilibrium=True)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "
Inline Exercise:\n",
- "Let us now add the CSTR (assign the name R101) and pass the following arguments:\n",
- "
\n",
- " - \"property_package\": m.fs.BTHM_params
\n",
- " - \"reaction_package\": m.fs.reaction_params
\n",
- " - \"has_heat_of_reaction\": True
\n",
- " - \"has_heat_transfer\": True
\n",
- "
\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 12,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Add reactor with the specifications above"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Let us now add the Flash (assign the name F101), Splitter (assign the name S101) and PressureChanger (assign the name C101)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 14,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Adding the flash tank F101 to the flowsheet\n",
- "m.fs.F101 = Flash(\n",
- " property_package=m.fs.BTHM_params, has_heat_transfer=True, has_pressure_change=True\n",
- ")\n",
- "\n",
- "# Adding the splitter S101 to the flowsheet\n",
- "m.fs.S101 = Splitter(\n",
- " property_package=m.fs.BTHM_params, outlet_list=[\"purge\", \"recycle\"]\n",
- ")\n",
- "\n",
- "# Adding the compressor C101 to the flowsheet\n",
- "m.fs.C101 = PressureChanger(\n",
- " property_package=m.fs.BTHM_params,\n",
- " compressor=True,\n",
- " thermodynamic_assumption=ThermodynamicAssumption.isothermal,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Remark\n",
- "\n",
- "Currently, the `SequentialDecomposition()` tool, which we will later be using to initialize the flowsheet, does not support the distillation column model. Thus, we will first simulate the flowsheet without the distillation column. After it converges, we will then add the distillation column, initialize it, and simulate the entire flowsheet."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "As mentioned above, we use the `m.fs.BTHM_params` package, which contains all the four species, for the reactor loop, and the simpler `m.fs.BT_params` for unit operations following the flash (i.e., heater H102 and the distillation column D101). We define a `Translator` block to link the source property package and the package it is to be translated to in the following manner:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 15,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Add translator block to convert between property packages\n",
- "m.fs.translator = Translator(\n",
- " inlet_property_package=m.fs.BTHM_params, outlet_property_package=m.fs.BT_params\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Translator block constraints\n",
- "\n",
- "The `Translator` block needs to know how to translate between the two property packages. This must be custom coded for each application because of the generality of the IDAES framework.\n",
- "\n",
- "For this process, five constraints are required based on the state variables used in the outgoing process.\n",
- "\n",
- "- Since we assumed that only benzene and toluene are present in the liquid phase, the total molar flowrate must be the sum of molar flowrates of benzene and toluene, respectively.\n",
- "- Temperature of the inlet and outlet streams must be the same.\n",
- "- Pressure of the inlet and outgoing streams must be the same\n",
- "- The mole fraction of benzene in the outgoing stream is the ratio of the molar flowrate of liquid benzene in the inlet to the sum of molar flowrates of liquid benzene and toluene in the inlet.\n",
- "- The mole fraction of toluene in the outgoing stream is the ratio of the molar flowrate of liquid toluene in the inlet to the sum of molar flowrates of liquid benzene and toluene in the inlet."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 16,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Add constraint: Total flow = benzene flow + toluene flow (molar)\n",
- "m.fs.translator.eq_total_flow = Constraint(\n",
- " expr=m.fs.translator.outlet.flow_mol[0]\n",
- " == m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
- " + m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
- ")\n",
- "\n",
- "# Add constraint: Outlet temperature = Inlet temperature\n",
- "m.fs.translator.eq_temperature = Constraint(\n",
- " expr=m.fs.translator.outlet.temperature[0] == m.fs.translator.inlet.temperature[0]\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "In the above, note that the variable flow_mol_phase_comp has the index - [time, phase, component]. As this is a steady-state flowsheet, the time index by default is 0. The valid phases are [\"Liq\", \"Vap\"]. Similarly the valid component list is [\"benzene\", \"toluene\", \"hydrogen\", \"methane\"]."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "Add the constraint to ensure that the outlet pressure is the same as the inlet pressure\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 17,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Add constraint: Outlet pressure = Inlet pressure"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 19,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Remaining constraints on the translator block\n",
- "\n",
- "# Add constraint: Benzene mole fraction definition\n",
- "m.fs.translator.eq_mole_frac_benzene = Constraint(\n",
- " expr=m.fs.translator.outlet.mole_frac_comp[0, \"benzene\"]\n",
- " == m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
- " / (\n",
- " m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
- " + m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
- " )\n",
- ")\n",
- "\n",
- "# Add constraint: Toluene mole fraction definition\n",
- "m.fs.translator.eq_mole_frac_toluene = Constraint(\n",
- " expr=m.fs.translator.outlet.mole_frac_comp[0, \"toluene\"]\n",
- " == m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
- " / (\n",
- " m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
- " + m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
- " )\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "Finally, let us add the Heater H102 in the same way as H101 but pass the m.fs.BT_params thermodynamic package. We will add the distillation column after converging the flowsheet.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 20,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Add the Heater H102 to the flowsheet"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Connecting Unit Models using Arcs\n",
- "\n",
- "We have now added the initial set of unit models to the flowsheet. However, we have not yet specifed how the units are connected. To do this, we will be using the `Arc` which is a pyomo component that takes in two arguments: `source` and `destination`. Let us connect the outlet of the mixer (M101) to the inlet of the heater (H101). "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 22,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.s03 = Arc(source=m.fs.M101.outlet, destination=m.fs.H101.inlet)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "![](HDA_flowsheet_distillation.png) \n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Now, connect the H101 outlet to the R101 inlet using the cell above as a guide. \n",
- "
\n",
- "\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 23,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Connect the H101 outlet to R101 inlet"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We will now be connecting the rest of the units as shown below. Notice how the outlet names are different for the flash tank as it has a vapor and a liquid outlet. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 25,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.s05 = Arc(source=m.fs.R101.outlet, destination=m.fs.F101.inlet)\n",
- "m.fs.s06 = Arc(source=m.fs.F101.vap_outlet, destination=m.fs.S101.inlet)\n",
- "m.fs.s08 = Arc(source=m.fs.S101.recycle, destination=m.fs.C101.inlet)\n",
- "m.fs.s09 = Arc(source=m.fs.C101.outlet, destination=m.fs.M101.vapor_recycle)\n",
- "m.fs.s10a = Arc(source=m.fs.F101.liq_outlet, destination=m.fs.translator.inlet)\n",
- "m.fs.s10b = Arc(source=m.fs.translator.outlet, destination=m.fs.H102.inlet)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We have now connected the unit model block using the arcs. However, each of these arcs link to ports on the two unit models that are connected. In this case, the ports consist of the state variables that need to be linked between the unit models. Pyomo provides a convenient method to write these equality constraints for us between two ports and this is done as follows:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 26,
- "metadata": {},
- "outputs": [],
- "source": [
- "TransformationFactory(\"network.expand_arcs\").apply_to(m)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Appending additional constraints to the model\n",
- "\n",
- "Now, we will see how we can add additional constraints to the model using `Constraint` from Pyomo.\n",
- "\n",
- "Consider the reactor R101. By default, the conversion of a component is not calculated when we simulate the flowsheet. If we are interested either in specifying or constraining the conversion value, we can add the following constraint to calculate the conversion:\n",
- "$$ \\text{Conversion of toluene} = \\frac{\\text{molar flow of toluene in the inlet} - \\text{molar flow of toluene in the outlet}}{\\text{molar flow of toluene in the inlet}} $$ \n",
- "\n",
- "We add the constraint to the model as shown below."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 27,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Define the conversion variables using 'Var'\n",
- "m.fs.R101.conversion = Var(initialize=0.75, bounds=(0, 1))\n",
- "\n",
- "# Append the constraint to the model\n",
- "m.fs.R101.conv_constraint = Constraint(\n",
- " expr=m.fs.R101.conversion * m.fs.R101.inlet.flow_mol_phase_comp[0, \"Vap\", \"toluene\"]\n",
- " == (\n",
- " m.fs.R101.inlet.flow_mol_phase_comp[0, \"Vap\", \"toluene\"]\n",
- " - m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"toluene\"]\n",
- " )\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Fixing feed conditions and Initializing the flowsheet\n",
- "\n",
- "Let us first check how many degrees of freedom exist for this flowsheet using the `degrees_of_freedom` tool we imported earlier. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 28,
- "metadata": {},
- "outputs": [],
- "source": [
- "print(degrees_of_freedom(m))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We will now be fixing the toluene feed stream to the conditions shown in the flowsheet above. Please note that though this is a pure toluene feed, the remaining components are still assigned a very small non-zero value to help with convergence and initializing. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 30,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"benzene\"].fix(1e-5)\n",
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"toluene\"].fix(1e-5)\n",
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"hydrogen\"].fix(1e-5)\n",
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"methane\"].fix(1e-5)\n",
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"benzene\"].fix(1e-5)\n",
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"toluene\"].fix(0.30)\n",
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"hydrogen\"].fix(1e-5)\n",
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"methane\"].fix(1e-5)\n",
- "m.fs.M101.toluene_feed.temperature.fix(303.2)\n",
- "m.fs.M101.toluene_feed.pressure.fix(350000)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Similarly, let us fix the hydrogen feed to the following conditions in the next cell:\n",
- " \n",
- " - FH2 = 0.30 mol/s
\n",
- " - FCH4 = 0.02 mol/s
\n",
- " - Remaining components = 1e-5 mol/s
\n",
- " - T = 303.2 K
\n",
- " - P = 350000 Pa
\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 31,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"benzene\"].fix(1e-5)\n",
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"toluene\"].fix(1e-5)\n",
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"hydrogen\"].fix(0.30)\n",
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"methane\"].fix(0.02)\n",
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"benzene\"].fix(1e-5)\n",
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"toluene\"].fix(1e-5)\n",
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"hydrogen\"].fix(1e-5)\n",
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"methane\"].fix(1e-5)\n",
- "m.fs.M101.hydrogen_feed.temperature.fix(303.2)\n",
- "m.fs.M101.hydrogen_feed.pressure.fix(350000)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Fixing unit model specifications\n",
- "\n",
- "Now that we have fixed our inlet feed conditions, we will now be fixing the operating conditions for the unit models in the flowsheet. Let us set the H101 outlet temperature to 600 K. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 32,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Fix the temperature of the outlet from the heater H101\n",
- "m.fs.H101.outlet.temperature.fix(600)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "
Inline Exercise:\n",
- "Set the conditions for the reactor R101 to the following conditions:\n",
- "
\n",
- " - `conversion` = 0.75
\n",
- " - `heat_duty` = 0
\n",
- "
\n",
- "\n",
- "Use Shift+Enter to run the cell once you have typed in your code. \n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 33,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Fix the 'conversion' of the reactor R101\n",
- "\n",
- "\n",
- "# Todo: Fix the 'heat_duty' of the reactor R101"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The Flash conditions for F101 can be set as follows. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 35,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Fix the temperature of the vapor outlet from F101\n",
- "m.fs.F101.vap_outlet.temperature.fix(325.0)\n",
- "\n",
- "# Fix the pressure drop in the flash F101\n",
- "m.fs.F101.deltaP.fix(0)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Let us fix the split fraction of the purge stream from the splitter S101 and the outlet pressure from the compressor C101"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 36,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Fix the split fraction of the 'purge' stream from S101\n",
- "m.fs.S101.split_fraction[0, \"purge\"].fix(0.2)\n",
- "\n",
- "# Fix the pressure of the outlet from the compressor C101\n",
- "m.fs.C101.outlet.pressure.fix(350000)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Finally, let us fix the temperature of the outlet from H102 and the pressure drop in H102 as the following"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 37,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Fix the temperature of the outlet from the heater H102\n",
- "m.fs.H102.outlet.temperature.fix(375)\n",
- "\n",
- "# Fix the pressure drop in the heater H102\n",
- "m.fs.H102.deltaP.fix(-200000)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "To avoid convergence issues associated with poorly scaled variables and/or constraints, we scale the variables and constraints corresponding to the heaters H101 and H102, flash F101 and the reactor R101. Scaling factors for the flow rates, temperature, pressure, etc. have been defined in the property package: `ideal_VLE.py` file. Here, we set scaling factors only for the heat duty of the heater, the reaction extent, heat duty and volume of the reactor."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 38,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Set scaling factors for heat duty, reaction extent and volume\n",
- "iscale.set_scaling_factor(m.fs.H101.control_volume.heat, 1e-2)\n",
- "iscale.set_scaling_factor(m.fs.R101.control_volume.heat, 1e-2)\n",
- "iscale.set_scaling_factor(m.fs.R101.control_volume.rate_reaction_extent, 1)\n",
- "iscale.set_scaling_factor(m.fs.R101.control_volume.volume, 1)\n",
- "iscale.set_scaling_factor(m.fs.F101.control_volume.heat, 1e-2)\n",
- "iscale.set_scaling_factor(m.fs.H102.control_volume.heat, 1e-2)\n",
- "\n",
- "# Set the scaling factors for the remaining variables and all constraints\n",
- "iscale.calculate_scaling_factors(m.fs.H101)\n",
- "iscale.calculate_scaling_factors(m.fs.R101)\n",
- "iscale.calculate_scaling_factors(m.fs.F101)\n",
- "iscale.calculate_scaling_factors(m.fs.H102)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "We have now defined all the feed conditions and the inputs required for the unit models. The system should now have 0 degrees of freedom i.e. should be a square problem. Please check that the degrees of freedom is 0. \n",
- "\n",
- "Use Shift+Enter to run the cell once you have typed in your code. \n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 39,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Check the degrees of freedom"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Initialization\n",
- "\n",
- "This subsection will demonstrate how to use the built-in sequential decomposition tool to initialize our flowsheet.\n",
- "\n",
- "Let us first create an object for the `SequentialDecomposition` and specify our options for this. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 42,
- "metadata": {},
- "outputs": [],
- "source": [
- "seq = SequentialDecomposition()\n",
- "seq.options.select_tear_method = \"heuristic\"\n",
- "seq.options.tear_method = \"Wegstein\"\n",
- "seq.options.iterLim = 3\n",
- "\n",
- "# Using the SD tool\n",
- "G = seq.create_graph(m)\n",
- "heuristic_tear_set = seq.tear_set_arcs(G, method=\"heuristic\")\n",
- "order = seq.calculation_order(G)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Which is the tear stream? Display tear set and order"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 43,
- "metadata": {},
- "outputs": [],
- "source": [
- "for o in heuristic_tear_set:\n",
- " print(o.name)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "What sequence did the SD tool determine to solve this flowsheet with the least number of tears? "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 44,
- "metadata": {},
- "outputs": [],
- "source": [
- "for o in order:\n",
- " print(o[0].name)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The SequentialDecomposition tool has determined that the tear stream is the mixer outlet (s03 in the Figure above). We will need to provide a reasonable guess for this.\n",
- "\n",
- "For the initial guess, we assume that the flowrate of the recycle stream (s09) is zero. Consequently, the flow rate of the stream s03 is simply the sum of the flowrates of the toluene feed and hydrogen feed streams. Further, since the temperature and the pressure of both the toluene and hydrogen feed streams are the same, we specify their values as the initial guess for the temperature and pressure of the stream s03."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 45,
- "metadata": {},
- "outputs": [],
- "source": [
- "tear_guesses = {\n",
- " \"flow_mol_phase_comp\": {\n",
- " (0, \"Vap\", \"benzene\"): 1e-5,\n",
- " (0, \"Vap\", \"toluene\"): 1e-5,\n",
- " (0, \"Vap\", \"hydrogen\"): 0.30,\n",
- " (0, \"Vap\", \"methane\"): 0.02,\n",
- " (0, \"Liq\", \"benzene\"): 1e-5,\n",
- " (0, \"Liq\", \"toluene\"): 0.30,\n",
- " (0, \"Liq\", \"hydrogen\"): 1e-5,\n",
- " (0, \"Liq\", \"methane\"): 1e-5,\n",
- " },\n",
- " \"temperature\": {0: 303},\n",
- " \"pressure\": {0: 350000},\n",
- "}\n",
- "\n",
- "# Pass the tear_guess to the SD tool\n",
- "seq.set_guesses_for(m.fs.H101.inlet, tear_guesses)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Next, we need to tell the tool how to initialize a particular unit. We will be writing a python function which takes in a \"unit\" and calls the initialize method on that unit."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 46,
- "metadata": {},
- "outputs": [],
- "source": [
- "def function(unit):\n",
- " unit.initialize(outlvl=idaeslog.INFO)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We are now ready to initialize our flowsheet in a sequential mode. Note that we specifically set the iteration limit to be 3 as we are trying to use this tool only to get a good set of initial values such that IPOPT can then take over and solve this flowsheet for us. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 47,
- "metadata": {
- "scrolled": false
- },
- "outputs": [],
- "source": [
- "seq.run(m, function)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "We have now initialized the flowsheet. Let us run the flowsheet in a simulation mode to look at the results. \n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 48,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Create the solver object\n",
- "solver = get_solver()\n",
- "\n",
- "# Solve the model\n",
- "results = solver.solve(m, tee=True)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Add distillation column \n",
- "\n",
- "As mentioned earlier, the `SequentialDecomposition` tool currently does not support the distillation column model. Thus, we have not included the distillation column in the flowsheet. Now that we have a converged flowsheet, we will add the distillation column and simulate the entire flowsheet. \n",
- "\n",
- "In the following, we will\n",
- "- Add the distillation column \n",
- "- Connect it to the heater \n",
- "- Add the necessary equality constraints\n",
- "- Propogate the state variable information from the outlet of the heater to the inlet of the distillation column \n",
- "- Fix the degrees of freedom of the distillation block (reflux ratio, boilup ratio, and condenser pressure)\n",
- "- Scale the control volume heat variables to help convergence\n",
- "- Initialize the distillation block.\n",
- "\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 50,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Add distillation column to the flowsheet\n",
- "m.fs.D101 = TrayColumn(\n",
- " number_of_trays=10,\n",
- " feed_tray_location=5,\n",
- " condenser_type=CondenserType.totalCondenser,\n",
- " condenser_temperature_spec=TemperatureSpec.atBubblePoint,\n",
- " property_package=m.fs.BT_params,\n",
- ")\n",
- "\n",
- "# Connect the outlet from the heater H102 to the distillation column\n",
- "m.fs.s11 = Arc(source=m.fs.H102.outlet, destination=m.fs.D101.feed)\n",
- "\n",
- "# Add the necessary equality constraints\n",
- "TransformationFactory(\"network.expand_arcs\").apply_to(m)\n",
- "\n",
- "# Propagate the state\n",
- "propagate_state(m.fs.s11)\n",
- "\n",
- "# Fix the reflux ratio, boilup ratio, and the condenser pressure\n",
- "m.fs.D101.condenser.reflux_ratio.fix(0.5)\n",
- "m.fs.D101.reboiler.boilup_ratio.fix(0.5)\n",
- "m.fs.D101.condenser.condenser_pressure.fix(150000)\n",
- "\n",
- "# set scaling factors\n",
- "# Set scaling factors for heat duty\n",
- "iscale.set_scaling_factor(m.fs.D101.condenser.control_volume.heat, 1e-2)\n",
- "iscale.set_scaling_factor(m.fs.D101.reboiler.control_volume.heat, 1e-2)\n",
- "\n",
- "# Set the scaling factors for the remaining variables and all constraints\n",
- "iscale.calculate_scaling_factors(m.fs.D101)\n",
- "\n",
- "# Initialize the distillation column\n",
- "m.fs.D101.initialize(outlvl=idaeslog.INFO)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Adding expressions to compute capital and operating costs\n",
- "\n",
- "In this section, we will add a few Expressions that allow us to evaluate the performance. Expressions provide a convenient way of calculating certain values that are a function of the variables defined in the model. For more details on Expressions, please refer to: https://pyomo.readthedocs.io/en/stable/pyomo_modeling_components/Expressions.html"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 51,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Expression to compute the total cooling cost\n",
- "m.fs.cooling_cost = Expression(\n",
- " expr=0.25e-7 * (-m.fs.F101.heat_duty[0])\n",
- " + 0.2e-7 * (-m.fs.D101.condenser.heat_duty[0])\n",
- ")\n",
- "\n",
- "# Expression to compute the total heating cost\n",
- "m.fs.heating_cost = Expression(\n",
- " expr=2.2e-7 * m.fs.H101.heat_duty[0]\n",
- " + 1.2e-7 * m.fs.H102.heat_duty[0]\n",
- " + 1.9e-7 * m.fs.D101.reboiler.heat_duty[0]\n",
- ")\n",
- "\n",
- "# Expression to compute the total operating cost\n",
- "m.fs.operating_cost = Expression(\n",
- " expr=(3600 * 24 * 365 * (m.fs.heating_cost + m.fs.cooling_cost))\n",
- ")\n",
- "\n",
- "# Expression to compute the total capital cost\n",
- "m.fs.capital_cost = Expression(expr=1e5 * m.fs.R101.volume[0])"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Solve the entire flowsheet"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 53,
- "metadata": {},
- "outputs": [],
- "source": [
- "solver.solve(m, tee=True)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Analyze the Results of the Square Problem\n",
- "\n",
- "How much is the total cost (operating cost + capital cost), operating cost, capital cost, benzene purity in the distillate from the distilation column, and conversion of toluene in the reactor?"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 55,
- "metadata": {},
- "outputs": [],
- "source": [
- "print(\"total cost = $\", value(m.fs.capital_cost) + value(m.fs.operating_cost))\n",
- "print(\"operating cost = $\", value(m.fs.operating_cost))\n",
- "print(\"capital cost = $\", value(m.fs.capital_cost))\n",
- "print()\n",
- "print(\n",
- " \"Distillate flowrate = \",\n",
- " value(m.fs.D101.condenser.distillate.flow_mol[0]()),\n",
- " \"mol/s\",\n",
- ")\n",
- "print(\n",
- " \"Benzene purity = \",\n",
- " 100 * value(m.fs.D101.condenser.distillate.mole_frac_comp[0, \"benzene\"]),\n",
- " \"%\",\n",
- ")\n",
- "print(\"Residue flowrate = \", value(m.fs.D101.reboiler.bottoms.flow_mol[0]()), \"mol/s\")\n",
- "print(\n",
- " \"Toluene purity = \",\n",
- " 100 * value(m.fs.D101.reboiler.bottoms.mole_frac_comp[0, \"toluene\"]),\n",
- " \"%\",\n",
- ")\n",
- "print()\n",
- "print(\"Conversion = \", 100 * value(m.fs.R101.conversion), \"%\")\n",
- "print()\n",
- "print(\n",
- " \"Overhead benzene loss in F101 = \",\n",
- " 100\n",
- " * value(m.fs.F101.vap_outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"])\n",
- " / value(m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]),\n",
- " \"%\",\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Get the state of the streams entering and leaving the reactor R101"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 57,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.R101.report()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Get the state of the streams entering and leaving the reactor R101"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 58,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.F101.report()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Next, let's look at how much benzene we are losing with the light gases out of F101. IDAES has tools for creating stream tables based on the `Arcs` and/or `Ports` in a flowsheet. Let us create and print a simple stream table showing the stream leaving the reactor and the vapor stream from F101.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "How much benzene are we losing in the F101 vapor outlet stream?\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 59,
- "metadata": {},
- "outputs": [],
- "source": [
- "from idaes.core.util.tables import (\n",
- " create_stream_table_dataframe,\n",
- " stream_table_dataframe_to_string,\n",
- ")\n",
- "\n",
- "st = create_stream_table_dataframe({\"Reactor\": m.fs.s05, \"Light Gases\": m.fs.s06})\n",
- "print(stream_table_dataframe_to_string(st))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "You can querry additional variables here if you like. \n",
- "\n",
- "Use Shift+Enter to run the cell once you have typed in your code. \n",
- "
"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Optimization\n",
- "\n",
- "\n",
- "We saw from the results above that the total operating cost for the base case was $442,297 per year. We are producing 0.162 mol/s of benzene at a purity of 89.5%. However, we are losing around 43.3% of benzene in F101 vapor outlet stream. \n",
- "\n",
- "Let us try to minimize this cost such that:\n",
- "- we are producing at least 0.18 mol/s of benzene as distillate i.e. our product stream\n",
- "- purity of benzene i.e. the mole fraction of benzene in the distillate is at least 99%\n",
- "- restricting the benzene loss in F101 vapor outlet to less than 20%\n",
- "\n",
- "For this problem, our decision variables are as follows:\n",
- "- H101 outlet temperature\n",
- "- R101 outlet temperature\n",
- "- F101 outlet temperature\n",
- "- H102 outlet temperature\n",
- "- Condenser pressure\n",
- "- reflux ratio\n",
- "- boilup ratio\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Let us declare our objective function for this problem. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 60,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.objective = Objective(expr=m.fs.operating_cost + m.fs.capital_cost)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Now, we need to unfix the decision variables as we had solved a square problem (degrees of freedom = 0) until now. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 61,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.H101.outlet.temperature.unfix()\n",
- "m.fs.R101.conversion.unfix()\n",
- "m.fs.F101.vap_outlet.temperature.unfix()\n",
- "m.fs.D101.condenser.condenser_pressure.unfix()\n",
- "m.fs.D101.condenser.reflux_ratio.unfix()\n",
- "m.fs.D101.reboiler.boilup_ratio.unfix()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "Let us now unfix the remaining variable: the temperature of the outlet from H102\n",
- "\n",
- "Use Shift+Enter to run the cell once you have typed in your code. \n",
- "
\n",
- "\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 62,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Unfix the temperature of the outlet from H102"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Next, we need to set bounds on these decision variables to values shown below:\n",
- "\n",
- " - H101 outlet temperature [500, 600] K\n",
- " - R101 outlet temperature [600, 900] K\n",
- " - F101 outlet temperature [298, 450] K\n",
- " - H102 outlet temperature [350, 400] K\n",
- " - D101 condenser pressure [101325, 150000] Pa\n",
- " - D101 reflux ratio [0.1, 5]\n",
- " - D101 boilup ratio [0.1, 5]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 64,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Set bounds on the temperature of the outlet from H101\n",
- "m.fs.H101.outlet.temperature[0].setlb(500)\n",
- "m.fs.H101.outlet.temperature[0].setub(600)\n",
- "\n",
- "# Set bounds on the temperature of the outlet from R101\n",
- "m.fs.R101.outlet.temperature[0].setlb(600)\n",
- "m.fs.R101.outlet.temperature[0].setub(900)\n",
- "\n",
- "# Set bounds on the volume of the reactor R101\n",
- "m.fs.R101.volume[0].setlb(0)\n",
- "\n",
- "# Set bounds on the temperature of the vapor outlet from F101\n",
- "m.fs.F101.vap_outlet.temperature[0].setlb(298)\n",
- "m.fs.F101.vap_outlet.temperature[0].setub(450.0)\n",
- "\n",
- "# Set bounds on the temperature of the outlet from H102\n",
- "m.fs.H102.outlet.temperature[0].setlb(350)\n",
- "m.fs.H102.outlet.temperature[0].setub(400)\n",
- "\n",
- "# Set bounds on the pressure inside the condenser\n",
- "m.fs.D101.condenser.condenser_pressure.setlb(101325)\n",
- "m.fs.D101.condenser.condenser_pressure.setub(150000)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "Now, set the bounds for the D101 reflux ratio and boilup ratio.\n",
- "\n",
- "Use Shift+Enter to run the cell once you have typed in your code. \n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 65,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Set bounds on the reflux ratio\n",
- "\n",
- "\n",
- "# Todo: Set bounds on the boilup ratio"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Now, the only things left to define are our constraints on overhead loss in F101, distillate flowrate and its purity. Let us first look at defining a constraint for the overhead loss in F101 where we are restricting the benzene leaving the vapor stream to less than 20 % of the benzene available in the reactor outlet. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 67,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Ensure that the overhead loss of benzene from F101 <= 20%\n",
- "m.fs.overhead_loss = Constraint(\n",
- " expr=m.fs.F101.vap_outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]\n",
- " <= 0.20 * m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "Now, add the constraint such that we are producing at least 0.18 mol/s of benzene in the product stream which is the distillate of D101. Let us name this constraint as m.fs.product_flow. \n",
- "\n",
- "Use Shift+Enter to run the cell once you have typed in your code. \n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 68,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Add minimum product flow constraint"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Let us add the final constraint on product purity or the mole fraction of benzene in the distillate such that it is at least greater than 99%. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 70,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.product_purity = Constraint(\n",
- " expr=m.fs.D101.condenser.distillate.mole_frac_comp[0, \"benzene\"] >= 0.99\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "We have now defined the optimization problem and we are now ready to solve this problem. \n",
- "\n",
- "\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 71,
- "metadata": {},
- "outputs": [],
- "source": [
- "results = solver.solve(m, tee=True)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Optimization Results\n",
- "\n",
- "Display the results and product specifications"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 73,
- "metadata": {},
- "outputs": [],
- "source": [
- "print(\"total cost = $\", value(m.fs.capital_cost) + value(m.fs.operating_cost))\n",
- "print(\"operating cost = $\", value(m.fs.operating_cost))\n",
- "print(\"capital cost = $\", value(m.fs.capital_cost))\n",
- "print()\n",
- "print(\n",
- " \"Distillate flowrate = \",\n",
- " value(m.fs.D101.condenser.distillate.flow_mol[0]()),\n",
- " \"mol/s\",\n",
- ")\n",
- "print(\n",
- " \"Benzene purity = \",\n",
- " 100 * value(m.fs.D101.condenser.distillate.mole_frac_comp[0, \"benzene\"]),\n",
- " \"%\",\n",
- ")\n",
- "print(\"Residue flowrate = \", value(m.fs.D101.reboiler.bottoms.flow_mol[0]()), \"mol/s\")\n",
- "print(\n",
- " \"Toluene purity = \",\n",
- " 100 * value(m.fs.D101.reboiler.bottoms.mole_frac_comp[0, \"toluene\"]),\n",
- " \"%\",\n",
- ")\n",
- "print()\n",
- "print(\"Conversion = \", 100 * value(m.fs.R101.conversion), \"%\")\n",
- "print()\n",
- "print(\n",
- " \"Overhead benzene loss in F101 = \",\n",
- " 100\n",
- " * value(m.fs.F101.vap_outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"])\n",
- " / value(m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]),\n",
- " \"%\",\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Display optimal values for the decision variables"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 75,
- "metadata": {},
- "outputs": [],
- "source": [
- "print(\"Optimal Values\")\n",
- "print()\n",
- "\n",
- "print(\"H101 outlet temperature = \", value(m.fs.H101.outlet.temperature[0]), \"K\")\n",
- "\n",
- "print()\n",
- "print(\"R101 outlet temperature = \", value(m.fs.R101.outlet.temperature[0]), \"K\")\n",
- "\n",
- "print()\n",
- "print(\"F101 outlet temperature = \", value(m.fs.F101.vap_outlet.temperature[0]), \"K\")\n",
- "\n",
- "print()\n",
- "print(\"H102 outlet temperature = \", value(m.fs.H102.outlet.temperature[0]), \"K\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Key Takeaways\n",
- "\n",
- "Observe that the optimization was able to reduce the yearly operating cost from \\\\$427,593 to \\\\$408,342 (~4.5%). However, the amortized capital cost more than doubled from \\\\$14,704 to \\\\$29,927 due to the need to increase the conversion in the reactor (from 75% to 93%) to meet the production and purity constraints. \n",
- "\n",
- "Further, observe that the product flow rate and product purity are at their minimum values (0.18 mol/s and 99%, respectively). This is expected as increasing recovery would require more energy and cost to purify the product.\n",
- "\n",
- "\n",
- "Finally, observe that the operating temperature of the flash (F101) is almost at its lower bound. This helps in minimizing the amount of benzene in the vapor stream leaving the flash."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
+ "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": [
+ "\n",
+ "# HDA Flowsheet Simulation and Optimization\n",
+ "Maintainer: Brandon Paul \n",
+ "Author: Brandon Paul \n",
+ "Updated: 2023-06-01 \n",
+ "\n",
+ "\n",
+ "## Note\n",
+ "\n",
+ "This tutorial will be similar to the HDA flowsheet tutorial in the Tutorials section, except that we use a distillation column instead of a second flash (F102) to produce benzene and toluene products.\n",
+ "\n",
+ "\n",
+ "## Learning outcomes\n",
+ "\n",
+ "\n",
+ "- Construct a steady-state flowsheet using the IDAES unit model library\n",
+ "- Connecting unit models in a flowsheet using Arcs\n",
+ "- Using the SequentialDecomposition tool to initialize a flowsheet with recycle\n",
+ "- Fomulate and solve an optimization problem\n",
+ " - Defining an objective function\n",
+ " - Setting variable bounds\n",
+ " - Adding additional constraints \n",
+ "\n",
+ "\n",
+ "## Problem Statement\n",
+ "\n",
+ "Hydrodealkylation is a chemical reaction that often involves reacting\n",
+ "an aromatic hydrocarbon in the presence of hydrogen gas to form a\n",
+ "simpler aromatic hydrocarbon devoid of functional groups. In this\n",
+ "example, toluene will be reacted with hydrogen gas at high temperatures\n",
+ " to form benzene via the following reaction:\n",
+ "\n",
+ "**C6H5CH3 + H2 → C6H6 + CH4**\n",
+ "\n",
+ "\n",
+ "This reaction is often accompanied by an equilibrium side reaction\n",
+ "which forms diphenyl, which we will neglect for this example.\n",
+ "\n",
+ "This example is based on the 1967 AIChE Student Contest problem as\n",
+ "present by Douglas, J.M., Chemical Design of Chemical Processes, 1988,\n",
+ "McGraw-Hill.\n",
+ "\n",
+ "The flowsheet that we will be using for this module is shown below with the stream conditions. We will be processing toluene and hydrogen to produce at least 370 TPY of benzene. As shown in the flowsheet, we use a flash tank, F101, to separate out the non-condensibles, and a distillation column, D101, to further separate the benzene-toluene mixture to improve the benzene purity. The non-condensibles separated out in F101 will be partially recycled back to M101 and the rest will be purged. We will assume ideal gas behavior for this flowsheet. The properties required for this module are defined in\n",
+ "\n",
+ "- `hda_ideal_VLE.py`\n",
+ "- `idaes.models.properties.activity_coeff_models.BTX_activity_coeff_VLE`\n",
+ "- `hda_reaction.py`\n",
+ "\n",
+ "We will be using two thermodynamic packages: one (first in the list above) containing all four components (i.e., toluene, hydrogen, benzene, and methane) and the other (second in the list above) containing benzene and toluene only. The latter is needed to simplify the VLE calculations in the distillation column model. \n",
+ "\n",
+ "![](HDA_flowsheet_distillation.png)\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Translator block\n",
+ "\n",
+ "Benzene and toluene are separated by distillation, so the process involves phase equilibrium and two-phase flow conditions. However, the presence of hydrogen and methane complicates the calculations. This is because, hydrogen and methane are non-condensable under all conditions of interest; ergo, a vapor phase will always be present, and the mixture bubble point is extremely low. To simplify the phase equilibrium calculations, hydrogen and methane will be considered completely as non-condensable and insoluble in the liquid outlet from the flash F101.\n",
+ "\n",
+ "Since no hydrogen and methane will be present in the unit operations following the flash, a different component list can be used to simplify the property calculations. IDAES supports the definition of multiple property packages within a single flowsheet via `Translator` blocks. `Translator` blocks convert between different property calculations, component lists, and equations of state. "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Importing required pyomo and idaes components\n",
+ "\n",
+ "\n",
+ "To construct a flowsheet, we will need several components from the pyomo and idaes package. Let us first import the following components from Pyomo:\n",
+ "- Constraint (to write constraints)\n",
+ "- Var (to declare variables)\n",
+ "- ConcreteModel (to create the concrete model object)\n",
+ "- Expression (to evaluate values as a function of variables defined in the model)\n",
+ "- Objective (to define an objective function for optimization)\n",
+ "- SolverFactory (to solve the problem)\n",
+ "- TransformationFactory (to apply certain transformations)\n",
+ "- Arc (to connect two unit models)\n",
+ "- SequentialDecomposition (to initialize the flowsheet in a sequential mode)\n",
+ "\n",
+ "For further details on these components, please refer to the pyomo documentation: https://pyomo.readthedocs.io/en/stable/\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from pyomo.environ import (\n",
+ " Constraint,\n",
+ " Var,\n",
+ " ConcreteModel,\n",
+ " Expression,\n",
+ " Objective,\n",
+ " TransformationFactory,\n",
+ " value,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "Import `Arc` and `SequentialDecomposition` tools from `pyomo.network`\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Import the above mentioned tools from pyomo.network"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "From IDAES, we will be needing the FlowsheetBlock and the following unit models:\n",
+ "- Mixer\n",
+ "- Heater\n",
+ "- CSTR\n",
+ "- Flash\n",
+ "- Separator (splitter) \n",
+ "- PressureChanger\n",
+ "- Translator (to switch from one property package to another)\n",
+ "- TrayColumn (distillation column)\n",
+ "- CondenserType (Type of the overhead condenser: complete or partial)\n",
+ "- TemperatureSpec (Temperature specification inside the condenser)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from idaes.core import FlowsheetBlock"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from idaes.models.unit_models import (\n",
+ " PressureChanger,\n",
+ " Mixer,\n",
+ " Separator as Splitter,\n",
+ " Heater,\n",
+ " CSTR,\n",
+ " Flash,\n",
+ " Translator,\n",
+ ")\n",
+ "\n",
+ "from idaes.models_extra.column_models import TrayColumn\n",
+ "from idaes.models_extra.column_models.condenser import CondenserType, TemperatureSpec"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We will also be needing some utility tools to put together the flowsheet and calculate the degrees of freedom. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Utility tools to put together the flowsheet and calculate the degrees of freedom\n",
+ "from idaes.models.unit_models.pressure_changer import ThermodynamicAssumption\n",
+ "from idaes.core.util.model_statistics import degrees_of_freedom\n",
+ "from idaes.core.util.initialization import propagate_state\n",
+ "from idaes.core.solvers import get_solver\n",
+ "import idaes.core.util.scaling as iscale\n",
+ "from idaes.core.util.exceptions import InitializationError\n",
+ "\n",
+ "# Import idaes logger to set output levels\n",
+ "import idaes.logger as idaeslog"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Importing required thermo and reaction packages\n",
+ "\n",
+ "Finally, we import the thermophysical (`ideal_VLE.py` and `BTXParameterBlock`) packages and reaction package (`reaction.py`) for the HDA process. We have created custom thermophysical packages that assume ideal gas behavior with support for VLE. The reaction package consists of the stochiometric coefficients for the reaction, heat of reaction, and kinetic information (Arrhenius constant and activation energy). "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from idaes_examples.mod.hda import hda_reaction as reaction_props\n",
+ "from idaes.models.properties.activity_coeff_models.BTX_activity_coeff_VLE import (\n",
+ " BTXParameterBlock,\n",
+ ")\n",
+ "\n",
+ "from idaes_examples.mod.hda.hda_ideal_VLE import HDAParameterBlock"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Constructing the Flowsheet\n",
+ "\n",
+ "We have now imported all the components, unit models, and property modules we need to construct a flowsheet. Let us create a ConcreteModel and add the flowsheet block to it. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Create a Pyomo Concrete Model to contain the problem\n",
+ "m = ConcreteModel()\n",
+ "\n",
+ "# Add a steady state flowsheet block to the model\n",
+ "m.fs = FlowsheetBlock(dynamic=False)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We will now add the thermophysical and reaction packages to the flowsheet."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Property package for benzene, toluene, hydrogen, methane mixture\n",
+ "m.fs.BTHM_params = HDAParameterBlock()\n",
+ "\n",
+ "# Property package for the benzene-toluene mixture\n",
+ "m.fs.BT_params = BTXParameterBlock(\n",
+ " valid_phase=(\"Liq\", \"Vap\"), activity_coeff_model=\"Ideal\"\n",
+ ")\n",
+ "\n",
+ "# Reaction package for the HDA reaction\n",
+ "m.fs.reaction_params = reaction_props.HDAReactionParameterBlock(\n",
+ " property_package=m.fs.BTHM_params\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Adding Unit Models\n",
+ "\n",
+ "Let us start adding the unit models we have imported to the flowsheet. Here, we are adding the Mixer (assigned a name M101) and a Heater (assigned a name H101). Note that, all unit models need to be given a property package argument. In addition, the Mixer unit model needs a `list` consisting of the inlets (toluene feed, hydrogen feed and vapor recycle streams in this case). "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Adding the mixer M101 to the flowsheet\n",
+ "m.fs.M101 = Mixer(\n",
+ " property_package=m.fs.BTHM_params,\n",
+ " inlet_list=[\"toluene_feed\", \"hydrogen_feed\", \"vapor_recycle\"],\n",
+ ")\n",
+ "\n",
+ "# Adding the heater H101 to the flowsheet\n",
+ "m.fs.H101 = Heater(property_package=m.fs.BTHM_params, has_phase_equilibrium=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "
Inline Exercise:\n",
+ "Let us now add the CSTR (assign the name R101) and pass the following arguments:\n",
+ "
\n",
+ " - \"property_package\": m.fs.BTHM_params
\n",
+ " - \"reaction_package\": m.fs.reaction_params
\n",
+ " - \"has_heat_of_reaction\": True
\n",
+ " - \"has_heat_transfer\": True
\n",
+ "
\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Add reactor with the specifications above"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let us now add the Flash (assign the name F101), Splitter (assign the name S101) and PressureChanger (assign the name C101)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Adding the flash tank F101 to the flowsheet\n",
+ "m.fs.F101 = Flash(\n",
+ " property_package=m.fs.BTHM_params, has_heat_transfer=True, has_pressure_change=True\n",
+ ")\n",
+ "\n",
+ "# Adding the splitter S101 to the flowsheet\n",
+ "m.fs.S101 = Splitter(\n",
+ " property_package=m.fs.BTHM_params, outlet_list=[\"purge\", \"recycle\"]\n",
+ ")\n",
+ "\n",
+ "# Adding the compressor C101 to the flowsheet\n",
+ "m.fs.C101 = PressureChanger(\n",
+ " property_package=m.fs.BTHM_params,\n",
+ " compressor=True,\n",
+ " thermodynamic_assumption=ThermodynamicAssumption.isothermal,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Remark\n",
+ "\n",
+ "Currently, the `SequentialDecomposition()` tool, which we will later be using to initialize the flowsheet, does not support the distillation column model. Thus, we will first simulate the flowsheet without the distillation column. After it converges, we will then add the distillation column, initialize it, and simulate the entire flowsheet."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As mentioned above, we use the `m.fs.BTHM_params` package, which contains all the four species, for the reactor loop, and the simpler `m.fs.BT_params` for unit operations following the flash (i.e., heater H102 and the distillation column D101). We define a `Translator` block to link the source property package and the package it is to be translated to in the following manner:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Add translator block to convert between property packages\n",
+ "m.fs.translator = Translator(\n",
+ " inlet_property_package=m.fs.BTHM_params, outlet_property_package=m.fs.BT_params\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Translator block constraints\n",
+ "\n",
+ "The `Translator` block needs to know how to translate between the two property packages. This must be custom coded for each application because of the generality of the IDAES framework.\n",
+ "\n",
+ "For this process, five constraints are required based on the state variables used in the outgoing process.\n",
+ "\n",
+ "- Since we assumed that only benzene and toluene are present in the liquid phase, the total molar flowrate must be the sum of molar flowrates of benzene and toluene, respectively.\n",
+ "- Temperature of the inlet and outlet streams must be the same.\n",
+ "- Pressure of the inlet and outgoing streams must be the same\n",
+ "- The mole fraction of benzene in the outgoing stream is the ratio of the molar flowrate of liquid benzene in the inlet to the sum of molar flowrates of liquid benzene and toluene in the inlet.\n",
+ "- The mole fraction of toluene in the outgoing stream is the ratio of the molar flowrate of liquid toluene in the inlet to the sum of molar flowrates of liquid benzene and toluene in the inlet."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Add constraint: Total flow = benzene flow + toluene flow (molar)\n",
+ "m.fs.translator.eq_total_flow = Constraint(\n",
+ " expr=m.fs.translator.outlet.flow_mol[0]\n",
+ " == m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
+ " + m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
+ ")\n",
+ "\n",
+ "# Add constraint: Outlet temperature = Inlet temperature\n",
+ "m.fs.translator.eq_temperature = Constraint(\n",
+ " expr=m.fs.translator.outlet.temperature[0] == m.fs.translator.inlet.temperature[0]\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In the above, note that the variable flow_mol_phase_comp has the index - [time, phase, component]. As this is a steady-state flowsheet, the time index by default is 0. The valid phases are [\"Liq\", \"Vap\"]. Similarly the valid component list is [\"benzene\", \"toluene\", \"hydrogen\", \"methane\"]."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "Add the constraint to ensure that the outlet pressure is the same as the inlet pressure\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Add constraint: Outlet pressure = Inlet pressure"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Remaining constraints on the translator block\n",
+ "\n",
+ "# Add constraint: Benzene mole fraction definition\n",
+ "m.fs.translator.eq_mole_frac_benzene = Constraint(\n",
+ " expr=m.fs.translator.outlet.mole_frac_comp[0, \"benzene\"]\n",
+ " == m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
+ " / (\n",
+ " m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
+ " + m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
+ " )\n",
+ ")\n",
+ "\n",
+ "# Add constraint: Toluene mole fraction definition\n",
+ "m.fs.translator.eq_mole_frac_toluene = Constraint(\n",
+ " expr=m.fs.translator.outlet.mole_frac_comp[0, \"toluene\"]\n",
+ " == m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
+ " / (\n",
+ " m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
+ " + m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
+ " )\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "Finally, let us add the Heater H102 in the same way as H101 but pass the m.fs.BT_params thermodynamic package. We will add the distillation column after converging the flowsheet.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 20,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Add the Heater H102 to the flowsheet"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Connecting Unit Models using Arcs\n",
+ "\n",
+ "We have now added the initial set of unit models to the flowsheet. However, we have not yet specified how the units are connected. To do this, we will be using the `Arc` which is a pyomo component that takes in two arguments: `source` and `destination`. Let us connect the outlet of the mixer (M101) to the inlet of the heater (H101). "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.fs.s03 = Arc(source=m.fs.M101.outlet, destination=m.fs.H101.inlet)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "![](HDA_flowsheet_distillation.png) \n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Now, connect the H101 outlet to the R101 inlet using the cell above as a guide. \n",
+ "
\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 23,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Connect the H101 outlet to R101 inlet"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We will now be connecting the rest of the units as shown below. Notice how the outlet names are different for the flash tank as it has a vapor and a liquid outlet. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.fs.s05 = Arc(source=m.fs.R101.outlet, destination=m.fs.F101.inlet)\n",
+ "m.fs.s06 = Arc(source=m.fs.F101.vap_outlet, destination=m.fs.S101.inlet)\n",
+ "m.fs.s08 = Arc(source=m.fs.S101.recycle, destination=m.fs.C101.inlet)\n",
+ "m.fs.s09 = Arc(source=m.fs.C101.outlet, destination=m.fs.M101.vapor_recycle)\n",
+ "m.fs.s10a = Arc(source=m.fs.F101.liq_outlet, destination=m.fs.translator.inlet)\n",
+ "m.fs.s10b = Arc(source=m.fs.translator.outlet, destination=m.fs.H102.inlet)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We have now connected the unit model block using the arcs. However, each of these arcs link to ports on the two unit models that are connected. In this case, the ports consist of the state variables that need to be linked between the unit models. Pyomo provides a convenient method to write these equality constraints for us between two ports and this is done as follows:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 26,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "TransformationFactory(\"network.expand_arcs\").apply_to(m)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Appending additional constraints to the model\n",
+ "\n",
+ "Now, we will see how we can add additional constraints to the model using `Constraint` from Pyomo.\n",
+ "\n",
+ "Consider the reactor R101. By default, the conversion of a component is not calculated when we simulate the flowsheet. If we are interested either in specifying or constraining the conversion value, we can add the following constraint to calculate the conversion:\n",
+ "$$ \\text{Conversion of toluene} = \\frac{\\text{molar flow of toluene in the inlet} - \\text{molar flow of toluene in the outlet}}{\\text{molar flow of toluene in the inlet}} $$ \n",
+ "\n",
+ "We add the constraint to the model as shown below."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 27,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Define the conversion variables using 'Var'\n",
+ "m.fs.R101.conversion = Var(initialize=0.75, bounds=(0, 1))\n",
+ "\n",
+ "# Append the constraint to the model\n",
+ "m.fs.R101.conv_constraint = Constraint(\n",
+ " expr=m.fs.R101.conversion * m.fs.R101.inlet.flow_mol_phase_comp[0, \"Vap\", \"toluene\"]\n",
+ " == (\n",
+ " m.fs.R101.inlet.flow_mol_phase_comp[0, \"Vap\", \"toluene\"]\n",
+ " - m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"toluene\"]\n",
+ " )\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Fixing feed conditions and Initializing the flowsheet\n",
+ "\n",
+ "Let us first check how many degrees of freedom exist for this flowsheet using the `degrees_of_freedom` tool we imported earlier. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 28,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "29\n"
+ ]
}
- ],
- "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.8.16"
+ ],
+ "source": [
+ "print(degrees_of_freedom(m))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We will now be fixing the toluene feed stream to the conditions shown in the flowsheet above. Please note that though this is a pure toluene feed, the remaining components are still assigned a very small non-zero value to help with convergence and initializing. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 30,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"benzene\"].fix(1e-5)\n",
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"toluene\"].fix(1e-5)\n",
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"hydrogen\"].fix(1e-5)\n",
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"methane\"].fix(1e-5)\n",
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"benzene\"].fix(1e-5)\n",
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"toluene\"].fix(0.30)\n",
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"hydrogen\"].fix(1e-5)\n",
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"methane\"].fix(1e-5)\n",
+ "m.fs.M101.toluene_feed.temperature.fix(303.2)\n",
+ "m.fs.M101.toluene_feed.pressure.fix(350000)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Similarly, let us fix the hydrogen feed to the following conditions in the next cell:\n",
+ " \n",
+ " - FH2 = 0.30 mol/s
\n",
+ " - FCH4 = 0.02 mol/s
\n",
+ " - Remaining components = 1e-5 mol/s
\n",
+ " - T = 303.2 K
\n",
+ " - P = 350000 Pa
\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 31,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"benzene\"].fix(1e-5)\n",
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"toluene\"].fix(1e-5)\n",
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"hydrogen\"].fix(0.30)\n",
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"methane\"].fix(0.02)\n",
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"benzene\"].fix(1e-5)\n",
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"toluene\"].fix(1e-5)\n",
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"hydrogen\"].fix(1e-5)\n",
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"methane\"].fix(1e-5)\n",
+ "m.fs.M101.hydrogen_feed.temperature.fix(303.2)\n",
+ "m.fs.M101.hydrogen_feed.pressure.fix(350000)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Fixing unit model specifications\n",
+ "\n",
+ "Now that we have fixed our inlet feed conditions, we will now be fixing the operating conditions for the unit models in the flowsheet. Let us set the H101 outlet temperature to 600 K. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 32,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Fix the temperature of the outlet from the heater H101\n",
+ "m.fs.H101.outlet.temperature.fix(600)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "
Inline Exercise:\n",
+ "Set the conditions for the reactor R101 to the following conditions:\n",
+ "
\n",
+ " - `conversion` = 0.75
\n",
+ " - `heat_duty` = 0
\n",
+ "
\n",
+ "\n",
+ "Use Shift+Enter to run the cell once you have typed in your code. \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 33,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Fix the 'conversion' of the reactor R101\n",
+ "\n",
+ "\n",
+ "# Todo: Fix the 'heat_duty' of the reactor R101"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The Flash conditions for F101 can be set as follows. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 35,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Fix the temperature of the vapor outlet from F101\n",
+ "m.fs.F101.vap_outlet.temperature.fix(325.0)\n",
+ "\n",
+ "# Fix the pressure drop in the flash F101\n",
+ "m.fs.F101.deltaP.fix(0)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let us fix the split fraction of the purge stream from the splitter S101 and the outlet pressure from the compressor C101"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 36,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Fix the split fraction of the 'purge' stream from S101\n",
+ "m.fs.S101.split_fraction[0, \"purge\"].fix(0.2)\n",
+ "\n",
+ "# Fix the pressure of the outlet from the compressor C101\n",
+ "m.fs.C101.outlet.pressure.fix(350000)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Finally, let us fix the temperature of the outlet from H102 and the pressure drop in H102 as the following"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 37,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Fix the temperature of the outlet from the heater H102\n",
+ "m.fs.H102.outlet.temperature.fix(375)\n",
+ "\n",
+ "# Fix the pressure drop in the heater H102\n",
+ "m.fs.H102.deltaP.fix(-200000)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "To avoid convergence issues associated with poorly scaled variables and/or constraints, we scale the variables and constraints corresponding to the heaters H101 and H102, flash F101 and the reactor R101. Scaling factors for the flow rates, temperature, pressure, etc. have been defined in the property package: `ideal_VLE.py` file. Here, we set scaling factors only for the heat duty of the heater, the reaction extent, heat duty and volume of the reactor."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 38,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enth_mol_phase_comp[Liq,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enth_mol_phase_comp[Liq,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enth_mol_phase_comp[Vap,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enth_mol_phase_comp[Vap,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].enth_mol_phase_comp[Liq,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].enth_mol_phase_comp[Liq,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].enth_mol_phase_comp[Vap,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].enth_mol_phase_comp[Vap,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].material_flow_terms[Liq,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].material_flow_terms[Vap,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].material_flow_terms[Liq,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].material_flow_terms[Vap,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enthalpy_flow_terms[Liq], enthalpy_flow_terms\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enthalpy_flow_terms[Vap], enthalpy_flow_terms\n"
+ ]
}
+ ],
+ "source": [
+ "# Set scaling factors for heat duty, reaction extent and volume\n",
+ "iscale.set_scaling_factor(m.fs.H101.control_volume.heat, 1e-2)\n",
+ "iscale.set_scaling_factor(m.fs.R101.control_volume.heat, 1e-2)\n",
+ "iscale.set_scaling_factor(m.fs.R101.control_volume.rate_reaction_extent, 1)\n",
+ "iscale.set_scaling_factor(m.fs.R101.control_volume.volume, 1)\n",
+ "iscale.set_scaling_factor(m.fs.F101.control_volume.heat, 1e-2)\n",
+ "iscale.set_scaling_factor(m.fs.H102.control_volume.heat, 1e-2)\n",
+ "\n",
+ "# Set the scaling factors for the remaining variables and all constraints\n",
+ "iscale.calculate_scaling_factors(m.fs.H101)\n",
+ "iscale.calculate_scaling_factors(m.fs.R101)\n",
+ "iscale.calculate_scaling_factors(m.fs.F101)\n",
+ "iscale.calculate_scaling_factors(m.fs.H102)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "We have now defined all the feed conditions and the inputs required for the unit models. The system should now have 0 degrees of freedom i.e. should be a square problem. Please check that the degrees of freedom is 0. \n",
+ "\n",
+ "Use Shift+Enter to run the cell once you have typed in your code. \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 39,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Check the degrees of freedom"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Initialization\n",
+ "\n",
+ "This subsection will demonstrate how to use the built-in sequential decomposition tool to initialize our flowsheet.\n",
+ "\n",
+ "Let us first create an object for the `SequentialDecomposition` and specify our options for this. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 42,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "seq = SequentialDecomposition()\n",
+ "seq.options.select_tear_method = \"heuristic\"\n",
+ "seq.options.tear_method = \"Wegstein\"\n",
+ "seq.options.iterLim = 3\n",
+ "\n",
+ "# Using the SD tool\n",
+ "G = seq.create_graph(m)\n",
+ "heuristic_tear_set = seq.tear_set_arcs(G, method=\"heuristic\")\n",
+ "order = seq.calculation_order(G)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Which is the tear stream? Display tear set and order"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 43,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "fs.s03\n"
+ ]
+ }
+ ],
+ "source": [
+ "for o in heuristic_tear_set:\n",
+ " print(o.name)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "What sequence did the SD tool determine to solve this flowsheet with the least number of tears? "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 44,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "fs.H101\n",
+ "fs.R101\n",
+ "fs.F101\n",
+ "fs.S101\n",
+ "fs.C101\n",
+ "fs.M101\n"
+ ]
+ }
+ ],
+ "source": [
+ "for o in order:\n",
+ " print(o[0].name)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The SequentialDecomposition tool has determined that the tear stream is the mixer outlet (s03 in the Figure above). We will need to provide a reasonable guess for this.\n",
+ "\n",
+ "For the initial guess, we assume that the flowrate of the recycle stream (s09) is zero. Consequently, the flow rate of the stream s03 is simply the sum of the flowrates of the toluene feed and hydrogen feed streams. Further, since the temperature and the pressure of both the toluene and hydrogen feed streams are the same, we specify their values as the initial guess for the temperature and pressure of the stream s03."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 45,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "tear_guesses = {\n",
+ " \"flow_mol_phase_comp\": {\n",
+ " (0, \"Vap\", \"benzene\"): 1e-5,\n",
+ " (0, \"Vap\", \"toluene\"): 1e-5,\n",
+ " (0, \"Vap\", \"hydrogen\"): 0.30,\n",
+ " (0, \"Vap\", \"methane\"): 0.02,\n",
+ " (0, \"Liq\", \"benzene\"): 1e-5,\n",
+ " (0, \"Liq\", \"toluene\"): 0.30,\n",
+ " (0, \"Liq\", \"hydrogen\"): 1e-5,\n",
+ " (0, \"Liq\", \"methane\"): 1e-5,\n",
+ " },\n",
+ " \"temperature\": {0: 303.2},\n",
+ " \"pressure\": {0: 350000},\n",
+ "}\n",
+ "\n",
+ "# Pass the tear_guess to the SD tool\n",
+ "seq.set_guesses_for(m.fs.H101.inlet, tear_guesses)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Next, we need to tell the tool how to initialize a particular unit. We will be writing a python function which takes in a \"unit\" and calls the initialize method on that unit. For the initialization, we will import a Block Triangularization Initializer which decomposes the model into a set of subproblems. These subproblems are solved using a block triangularization transformation before applying a simple Newton or user-selected solver. Methods such as block triangularization often solve faster and yield more reliable behavior than heuristic methods, but sometime struggle to decompose models with strongly coupled equations (e.g. column models, systems with counter-current flow, vapor-liquid equilibrium)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 46,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def function(unit):\n",
+ " # Try initializing using default initializer,\n",
+ " # if it fails (probably due to scaling) try for the second time\n",
+ " try:\n",
+ " initializer = unit.default_initializer()\n",
+ " initializer.initialize(unit, output_level=idaeslog.INFO)\n",
+ " except InitializationError:\n",
+ " solver = get_solver()\n",
+ " solver.solve(unit)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We are now ready to initialize our flowsheet in a sequential mode. Note that we specifically set the iteration limit to be 3 as we are trying to use this tool only to get a good set of initial values such that IPOPT can then take over and solve this flowsheet for us. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 47,
+ "metadata": {
+ "scrolled": false
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2024-08-28 18:38:14 [INFO] idaes.init.fs.H101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:16 [INFO] idaes.init.fs.H101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:16 [INFO] idaes.init.fs.R101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:16 [INFO] idaes.init.fs.R101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.F101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.F101.control_volume.properties_out: Initialization Complete\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 12\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 50 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.S101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - \n",
+ "2024-08-28 18:38:18 [INFO] idaes.init.fs.C101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:18 [INFO] idaes.init.fs.C101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:18 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 1 optimal - .\n",
+ "2024-08-28 18:38:18 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 2 optimal - .\n",
+ "2024-08-28 18:38:19 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 3 optimal - .\n",
+ "2024-08-28 18:38:19 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 4 optimal - .\n",
+ "2024-08-28 18:38:19 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 5 optimal - .\n",
+ "2024-08-28 18:38:19 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 1 optimal - .\n",
+ "2024-08-28 18:38:19 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 2 optimal - .\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 3 optimal - .\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 4 optimal - .\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 5 optimal - .\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.M101.toluene_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.M101.hydrogen_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.M101.vapor_recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:21 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:21 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - \n",
+ "2024-08-28 18:38:21 [INFO] idaes.init.fs.H101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:21 [INFO] idaes.init.fs.H101.control_volume.properties_out: Initialization Complete\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 11\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 50 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.R101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.R101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.F101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.F101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.S101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - \n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.C101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.C101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.M101.toluene_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.M101.hydrogen_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.M101.vapor_recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - \n",
+ "2024-08-28 18:38:24 [INFO] idaes.init.fs.H101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:24 [INFO] idaes.init.fs.H101.control_volume.properties_out: Initialization Complete\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 11\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 50 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "2024-08-28 18:38:24 [INFO] idaes.init.fs.R101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:24 [INFO] idaes.init.fs.R101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.F101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.F101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.S101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - \n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.C101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.C101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.M101.toluene_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.M101.hydrogen_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.M101.vapor_recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - \n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.H101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.H101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.R101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.R101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.F101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.F101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.S101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - \n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.C101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.C101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.M101.toluene_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.M101.hydrogen_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.M101.vapor_recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - \n",
+ "2024-08-28 18:38:29 [INFO] idaes.init.fs.H101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:29 [INFO] idaes.init.fs.H101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:29 [INFO] idaes.init.fs.R101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:29 [INFO] idaes.init.fs.R101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.F101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.F101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.S101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - \n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.C101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.C101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.M101.toluene_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.M101.hydrogen_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:31 [INFO] idaes.init.fs.M101.vapor_recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:31 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:31 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - \n",
+ "WARNING: Wegstein failed to converge in 3 iterations\n",
+ "2024-08-28 18:38:31 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 1 optimal - .\n",
+ "2024-08-28 18:38:31 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 2 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 3 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 4 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 5 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 1 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 2 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 3 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 4 optimal - .\n",
+ "2024-08-28 18:38:33 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 5 optimal - .\n"
+ ]
+ }
+ ],
+ "source": [
+ "seq.run(m, function)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "We have now initialized the flowsheet. Let us run the flowsheet in a simulation mode to look at the results. \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 48,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "WARNING: model contains export suffix 'scaling_factor' that contains 6\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 150 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "Ipopt 3.13.2: nlp_scaling_method=gradient-based\n",
+ "tol=1e-06\n",
+ "max_iter=200\n",
+ "\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...: 1097\n",
+ "Number of nonzeros in inequality constraint Jacobian.: 0\n",
+ "Number of nonzeros in Lagrangian Hessian.............: 877\n",
+ "\n",
+ "Total number of variables............................: 363\n",
+ " variables with only lower bounds: 8\n",
+ " variables with lower and upper bounds: 155\n",
+ " variables with only upper bounds: 0\n",
+ "Total number of equality constraints.................: 363\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.82e+04 1.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
+ " 1 0.0000000e+00 8.69e+03 1.44e+03 -1.0 2.00e+04 - 9.71e-01 4.67e-01H 1\n",
+ " 2 0.0000000e+00 1.29e+03 1.56e+03 -1.0 1.60e+04 - 9.79e-01 4.90e-01h 1\n",
+ " 3 0.0000000e+00 1.18e+03 1.55e+05 -1.0 1.40e+04 - 9.90e-01 4.99e-01h 1\n",
+ " 4 0.0000000e+00 5.46e+02 2.32e+09 -1.0 8.42e+03 - 1.00e+00 9.82e-01h 1\n",
+ " 5 0.0000000e+00 5.46e+03 3.66e+10 -1.0 5.97e+02 - 1.00e+00 9.90e-01h 1\n",
+ " 6 0.0000000e+00 1.21e+03 8.01e+09 -1.0 5.75e+00 - 1.00e+00 1.00e+00h 1\n",
+ " 7 0.0000000e+00 6.41e+00 3.87e+07 -1.0 1.53e-03 - 1.00e+00 1.00e+00f 1\n",
+ " 8 0.0000000e+00 1.96e-04 9.36e+02 -1.0 7.28e-06 - 1.00e+00 1.00e+00h 1\n",
+ " 9 0.0000000e+00 2.24e-08 4.99e-01 -3.8 5.92e-08 - 1.00e+00 1.00e+00h 1\n",
+ "Cannot recompute multipliers for feasibility problem. Error in eq_mult_calculator\n",
+ "\n",
+ "Number of Iterations....: 9\n",
+ "\n",
+ " (scaled) (unscaled)\n",
+ "Objective...............: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Dual infeasibility......: 1.5042487592972509e+04 1.5042487592972509e+04\n",
+ "Constraint violation....: 2.9103830456733704e-11 2.2351741790771484e-08\n",
+ "Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Overall NLP error.......: 2.9103830456733704e-11 1.5042487592972509e+04\n",
+ "\n",
+ "\n",
+ "Number of objective function evaluations = 11\n",
+ "Number of objective gradient evaluations = 10\n",
+ "Number of equality constraint evaluations = 11\n",
+ "Number of inequality constraint evaluations = 0\n",
+ "Number of equality constraint Jacobian evaluations = 10\n",
+ "Number of inequality constraint Jacobian evaluations = 0\n",
+ "Number of Lagrangian Hessian evaluations = 9\n",
+ "Total CPU secs in IPOPT (w/o function evaluations) = 0.011\n",
+ "Total CPU secs in NLP function evaluations = 0.001\n",
+ "\n",
+ "EXIT: Optimal Solution Found.\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Create the solver object\n",
+ "solver = get_solver()\n",
+ "\n",
+ "# Solve the model\n",
+ "results = solver.solve(m, tee=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Add distillation column \n",
+ "\n",
+ "As mentioned earlier, the `SequentialDecomposition` tool currently does not support the distillation column model. Thus, we have not included the distillation column in the flowsheet. Now that we have a converged flowsheet, we will add the distillation column and simulate the entire flowsheet. \n",
+ "\n",
+ "In the following, we will\n",
+ "- Add the distillation column \n",
+ "- Connect it to the heater \n",
+ "- Add the necessary equality constraints\n",
+ "- Propagate the state variable information from the outlet of the heater to the inlet of the distillation column \n",
+ "- Fix the degrees of freedom of the distillation block (reflux ratio, boilup ratio, and condenser pressure)\n",
+ "- Scale the control volume heat variables to help convergence\n",
+ "- Initialize the distillation block.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 50,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].material_flow_terms[Liq,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].material_flow_terms[Vap,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].material_flow_terms[Liq,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].material_flow_terms[Vap,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].enthalpy_flow_terms[Liq], enthalpy_flow_terms\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].enthalpy_flow_terms[Vap], enthalpy_flow_terms\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].material_flow_terms[Liq,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].material_flow_terms[Vap,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].material_flow_terms[Liq,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].material_flow_terms[Vap,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].enthalpy_flow_terms[Liq], enthalpy_flow_terms\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].enthalpy_flow_terms[Vap], enthalpy_flow_terms\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.e_flow_reflux[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.e_mole_frac_reflux[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.e_mole_frac_reflux[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.e_flow_vapor_reboil[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.e_mole_frac_vapor_reboil[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.e_mole_frac_vapor_reboil[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101: Begin initialization.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray: Begin initialization.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: State Released.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: State Released.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser.control_volume: Initialization Complete\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser: Initialization Complete, optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: State Released.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: State Released.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler: Initialization Complete, optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: State Released.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.rectification_section[1]: Begin initialization.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: State Released.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[2]: Begin initialization.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: State Released.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[2]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[2]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3]: Begin initialization.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: State Released.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[3]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[3]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[3]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4]: Begin initialization.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: State Released.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.stripping_section[6]: Begin initialization.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: State Released.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[7]: Begin initialization.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: State Released.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[7]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[7]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8]: Begin initialization.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: State Released.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[8]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[8]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[8]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9]: Begin initialization.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: State Released.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10]: Begin initialization.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: State Released.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: State Released.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101: Rectification section initialization status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101: Stripping section initialization status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: State Released.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: State Released.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101: Column section initialization status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: State Released.\n",
+ "2024-08-28 18:39:02 [INFO] idaes.init.fs.D101: Column section + Condenser initialization status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:02 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: State Released.\n",
+ "2024-08-28 18:39:03 [INFO] idaes.init.fs.D101: Column section + Condenser + Reboiler initialization status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:03 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: State Released.\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Add distillation column to the flowsheet\n",
+ "m.fs.D101 = TrayColumn(\n",
+ " number_of_trays=10,\n",
+ " feed_tray_location=5,\n",
+ " condenser_type=CondenserType.totalCondenser,\n",
+ " condenser_temperature_spec=TemperatureSpec.atBubblePoint,\n",
+ " property_package=m.fs.BT_params,\n",
+ ")\n",
+ "\n",
+ "# Connect the outlet from the heater H102 to the distillation column\n",
+ "m.fs.s11 = Arc(source=m.fs.H102.outlet, destination=m.fs.D101.feed)\n",
+ "\n",
+ "# Add the necessary equality constraints\n",
+ "TransformationFactory(\"network.expand_arcs\").apply_to(m)\n",
+ "\n",
+ "# Propagate the state\n",
+ "propagate_state(m.fs.s11)\n",
+ "\n",
+ "# Fix the reflux ratio, boilup ratio, and the condenser pressure\n",
+ "m.fs.D101.condenser.reflux_ratio.fix(0.5)\n",
+ "m.fs.D101.reboiler.boilup_ratio.fix(0.5)\n",
+ "m.fs.D101.condenser.condenser_pressure.fix(150000)\n",
+ "\n",
+ "# set scaling factors\n",
+ "# Set scaling factors for heat duty\n",
+ "iscale.set_scaling_factor(m.fs.D101.condenser.control_volume.heat, 1e-2)\n",
+ "iscale.set_scaling_factor(m.fs.D101.reboiler.control_volume.heat, 1e-2)\n",
+ "\n",
+ "# Set the scaling factors for the remaining variables and all constraints\n",
+ "iscale.calculate_scaling_factors(m.fs.D101)\n",
+ "\n",
+ "# Initialize the distillation column\n",
+ "m.fs.D101.initialize(outlvl=idaeslog.INFO)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Adding expressions to compute capital and operating costs\n",
+ "\n",
+ "In this section, we will add a few Expressions that allow us to evaluate the performance. Expressions provide a convenient way of calculating certain values that are a function of the variables defined in the model. For more details on Expressions, please refer to: https://pyomo.readthedocs.io/en/stable/pyomo_modeling_components/Expressions.html"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 51,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Expression to compute the total cooling cost\n",
+ "m.fs.cooling_cost = Expression(\n",
+ " expr=0.25e-7 * (-m.fs.F101.heat_duty[0])\n",
+ " + 0.2e-7 * (-m.fs.D101.condenser.heat_duty[0])\n",
+ ")\n",
+ "\n",
+ "# Expression to compute the total heating cost\n",
+ "m.fs.heating_cost = Expression(\n",
+ " expr=2.2e-7 * m.fs.H101.heat_duty[0]\n",
+ " + 1.2e-7 * m.fs.H102.heat_duty[0]\n",
+ " + 1.9e-7 * m.fs.D101.reboiler.heat_duty[0]\n",
+ ")\n",
+ "\n",
+ "# Expression to compute the total operating cost\n",
+ "m.fs.operating_cost = Expression(\n",
+ " expr=(3600 * 24 * 365 * (m.fs.heating_cost + m.fs.cooling_cost))\n",
+ ")\n",
+ "\n",
+ "# Expression to compute the total capital cost\n",
+ "m.fs.capital_cost = Expression(expr=1e5 * m.fs.R101.volume[0])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Solve the entire flowsheet"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 53,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "WARNING: model contains export suffix 'scaling_factor' that contains 7\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 150 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "Ipopt 3.13.2: nlp_scaling_method=gradient-based\n",
+ "tol=1e-06\n",
+ "max_iter=200\n",
+ "\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...: 4042\n",
+ "Number of nonzeros in inequality constraint Jacobian.: 0\n",
+ "Number of nonzeros in Lagrangian Hessian.............: 2376\n",
+ "\n",
+ "Total number of variables............................: 1169\n",
+ " variables with only lower bounds: 112\n",
+ " variables with lower and upper bounds: 365\n",
+ " variables with only upper bounds: 0\n",
+ "Total number of equality constraints.................: 1169\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.83e+04 1.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
+ " 1 0.0000000e+00 8.70e+03 1.50e+03 -1.0 3.69e+04 - 9.71e-01 4.62e-01H 1\n",
+ " 2 0.0000000e+00 1.53e+03 1.56e+03 -1.0 6.75e+03 - 9.77e-01 4.89e-01h 1\n",
+ " 3 0.0000000e+00 1.37e+03 1.55e+05 -1.0 9.37e+03 - 9.90e-01 4.99e-01h 1\n",
+ " 4 0.0000000e+00 6.14e+02 2.31e+09 -1.0 6.09e+03 - 1.00e+00 9.81e-01h 1\n",
+ " 5 0.0000000e+00 5.32e+03 3.62e+10 -1.0 5.56e+02 - 1.00e+00 9.90e-01h 1\n",
+ " 6 0.0000000e+00 1.16e+03 7.80e+09 -1.0 5.36e+00 - 1.00e+00 1.00e+00h 1\n",
+ " 7 0.0000000e+00 5.96e+00 3.64e+07 -1.0 1.47e-03 - 1.00e+00 1.00e+00f 1\n",
+ " 8 0.0000000e+00 1.69e-04 8.15e+02 -1.0 6.77e-06 - 1.00e+00 1.00e+00h 1\n",
+ " 9 0.0000000e+00 7.45e-09 6.64e-03 -3.8 2.00e-07 - 1.00e+00 1.00e+00h 1\n",
+ "Cannot recompute multipliers for feasibility problem. Error in eq_mult_calculator\n",
+ "\n",
+ "Number of Iterations....: 9\n",
+ "\n",
+ " (scaled) (unscaled)\n",
+ "Objective...............: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Dual infeasibility......: 1.5042483516409773e+04 1.5042483516409773e+04\n",
+ "Constraint violation....: 2.9103830456733704e-11 7.4505805969238281e-09\n",
+ "Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Overall NLP error.......: 2.9103830456733704e-11 1.5042483516409773e+04\n",
+ "\n",
+ "\n",
+ "Number of objective function evaluations = 11\n",
+ "Number of objective gradient evaluations = 10\n",
+ "Number of equality constraint evaluations = 11\n",
+ "Number of inequality constraint evaluations = 0\n",
+ "Number of equality constraint Jacobian evaluations = 10\n",
+ "Number of inequality constraint Jacobian evaluations = 0\n",
+ "Number of Lagrangian Hessian evaluations = 9\n",
+ "Total CPU secs in IPOPT (w/o function evaluations) = 0.083\n",
+ "Total CPU secs in NLP function evaluations = 0.013\n",
+ "\n",
+ "EXIT: Optimal Solution Found.\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "{'Problem': [{'Lower bound': -inf, 'Upper bound': inf, 'Number of objectives': 1, 'Number of constraints': 1169, 'Number of variables': 1169, 'Sense': 'unknown'}], 'Solver': [{'Status': 'ok', 'Message': 'Ipopt 3.13.2\\\\x3a Optimal Solution Found', 'Termination condition': 'optimal', 'Id': 0, 'Error rc': 0, 'Time': 0.2022566795349121}], 'Solution': [OrderedDict([('number of solutions', 0), ('number of solutions displayed', 0)])]}"
+ ]
+ },
+ "execution_count": 53,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "solver.solve(m, tee=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Analyze the Results of the Square Problem\n",
+ "\n",
+ "How much is the total cost (operating cost + capital cost), operating cost, capital cost, benzene purity in the distillate from the distilation column, and conversion of toluene in the reactor?"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 55,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "total cost = $ 442301.47075252194\n",
+ "operating cost = $ 427596.73056805483\n",
+ "capital cost = $ 14704.740184467111\n",
+ "\n",
+ "Distillate flowrate = 0.16196898920633368 mol/s\n",
+ "Benzene purity = 89.4916166580088 %\n",
+ "Residue flowrate = 0.10515007120697904 mol/s\n",
+ "Toluene purity = 43.32260291055251 %\n",
+ "\n",
+ "Conversion = 75.0 %\n",
+ "\n",
+ "Overhead benzene loss in F101 = 42.161938483603194 %\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(\"total cost = $\", value(m.fs.capital_cost) + value(m.fs.operating_cost))\n",
+ "print(\"operating cost = $\", value(m.fs.operating_cost))\n",
+ "print(\"capital cost = $\", value(m.fs.capital_cost))\n",
+ "print()\n",
+ "print(\n",
+ " \"Distillate flowrate = \",\n",
+ " value(m.fs.D101.condenser.distillate.flow_mol[0]()),\n",
+ " \"mol/s\",\n",
+ ")\n",
+ "print(\n",
+ " \"Benzene purity = \",\n",
+ " 100 * value(m.fs.D101.condenser.distillate.mole_frac_comp[0, \"benzene\"]),\n",
+ " \"%\",\n",
+ ")\n",
+ "print(\"Residue flowrate = \", value(m.fs.D101.reboiler.bottoms.flow_mol[0]()), \"mol/s\")\n",
+ "print(\n",
+ " \"Toluene purity = \",\n",
+ " 100 * value(m.fs.D101.reboiler.bottoms.mole_frac_comp[0, \"toluene\"]),\n",
+ " \"%\",\n",
+ ")\n",
+ "print()\n",
+ "print(\"Conversion = \", 100 * value(m.fs.R101.conversion), \"%\")\n",
+ "print()\n",
+ "print(\n",
+ " \"Overhead benzene loss in F101 = \",\n",
+ " 100\n",
+ " * value(m.fs.F101.vap_outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"])\n",
+ " / value(m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]),\n",
+ " \"%\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Get the state of the streams entering and leaving the reactor R101"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 57,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "====================================================================================\n",
+ "Unit : fs.R101 Time: 0.0\n",
+ "------------------------------------------------------------------------------------\n",
+ " Unit Performance\n",
+ "\n",
+ " Variables: \n",
+ "\n",
+ " Key : Value : Units : Fixed : Bounds\n",
+ " Heat Duty : 0.0000 : watt : True : (None, None)\n",
+ " Volume : 0.14705 : meter ** 3 : False : (None, None)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ " Stream Table\n",
+ " Units Inlet Outlet \n",
+ " flow_mol_phase_comp ('Liq', 'benzene') mole / second 1.2993e-07 1.2993e-07\n",
+ " flow_mol_phase_comp ('Liq', 'toluene') mole / second 8.4147e-07 8.4147e-07\n",
+ " flow_mol_phase_comp ('Liq', 'methane') mole / second 1.0000e-12 1.0000e-12\n",
+ " flow_mol_phase_comp ('Liq', 'hydrogen') mole / second 1.0000e-12 1.0000e-12\n",
+ " flow_mol_phase_comp ('Vap', 'benzene') mole / second 0.11936 0.35374\n",
+ " flow_mol_phase_comp ('Vap', 'toluene') mole / second 0.31252 0.078129\n",
+ " flow_mol_phase_comp ('Vap', 'methane') mole / second 1.0377 1.2721\n",
+ " flow_mol_phase_comp ('Vap', 'hydrogen') mole / second 0.56260 0.32821\n",
+ " temperature kelvin 600.00 771.85\n",
+ " pressure pascal 3.5000e+05 3.5000e+05\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "m.fs.R101.report()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Get the state of the streams entering and leaving the reactor R101"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 58,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "====================================================================================\n",
+ "Unit : fs.F101 Time: 0.0\n",
+ "------------------------------------------------------------------------------------\n",
+ " Unit Performance\n",
+ "\n",
+ " Variables: \n",
+ "\n",
+ " Key : Value : Units : Fixed : Bounds\n",
+ " Heat Duty : -70343. : watt : False : (None, None)\n",
+ " Pressure Change : 0.0000 : pascal : True : (None, None)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ " Stream Table\n",
+ " Units Inlet Vapor Outlet Liquid Outlet\n",
+ " flow_mol_phase_comp ('Liq', 'benzene') mole / second 1.2993e-07 1.0000e-08 0.20460 \n",
+ " flow_mol_phase_comp ('Liq', 'toluene') mole / second 8.4147e-07 1.0000e-08 0.062520 \n",
+ " flow_mol_phase_comp ('Liq', 'methane') mole / second 1.0000e-12 1.0000e-08 2.6712e-07 \n",
+ " flow_mol_phase_comp ('Liq', 'hydrogen') mole / second 1.0000e-12 1.0000e-08 2.6712e-07 \n",
+ " flow_mol_phase_comp ('Vap', 'benzene') mole / second 0.35374 0.14915 1.0000e-08 \n",
+ " flow_mol_phase_comp ('Vap', 'toluene') mole / second 0.078129 0.015610 1.0000e-08 \n",
+ " flow_mol_phase_comp ('Vap', 'methane') mole / second 1.2721 1.2721 1.0000e-08 \n",
+ " flow_mol_phase_comp ('Vap', 'hydrogen') mole / second 0.32821 0.32821 1.0000e-08 \n",
+ " temperature kelvin 771.85 325.00 325.00 \n",
+ " pressure pascal 3.5000e+05 3.5000e+05 3.5000e+05 \n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "m.fs.F101.report()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Next, let's look at how much benzene we are losing with the light gases out of F101. IDAES has tools for creating stream tables based on the `Arcs` and/or `Ports` in a flowsheet. Let us create and print a simple stream table showing the stream leaving the reactor and the vapor stream from F101.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "How much benzene are we losing in the F101 vapor outlet stream?\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 59,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ " Units Reactor Light Gases\n",
+ "flow_mol_phase_comp ('Liq', 'benzene') mole / second 1.2993e-07 1.0000e-08 \n",
+ "flow_mol_phase_comp ('Liq', 'toluene') mole / second 8.4147e-07 1.0000e-08 \n",
+ "flow_mol_phase_comp ('Liq', 'methane') mole / second 1.0000e-12 1.0000e-08 \n",
+ "flow_mol_phase_comp ('Liq', 'hydrogen') mole / second 1.0000e-12 1.0000e-08 \n",
+ "flow_mol_phase_comp ('Vap', 'benzene') mole / second 0.35374 0.14915 \n",
+ "flow_mol_phase_comp ('Vap', 'toluene') mole / second 0.078129 0.015610 \n",
+ "flow_mol_phase_comp ('Vap', 'methane') mole / second 1.2721 1.2721 \n",
+ "flow_mol_phase_comp ('Vap', 'hydrogen') mole / second 0.32821 0.32821 \n",
+ "temperature kelvin 771.85 325.00 \n",
+ "pressure pascal 3.5000e+05 3.5000e+05 \n"
+ ]
+ }
+ ],
+ "source": [
+ "from idaes.core.util.tables import (\n",
+ " create_stream_table_dataframe,\n",
+ " stream_table_dataframe_to_string,\n",
+ ")\n",
+ "\n",
+ "st = create_stream_table_dataframe({\"Reactor\": m.fs.s05, \"Light Gases\": m.fs.s06})\n",
+ "print(stream_table_dataframe_to_string(st))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "You can query additional variables here if you like. \n",
+ "\n",
+ "Use Shift+Enter to run the cell once you have typed in your code. \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Optimization\n",
+ "\n",
+ "\n",
+ "We saw from the results above that the total operating cost for the base case was $442,297 per year. We are producing 0.162 mol/s of benzene at a purity of 89.5%. However, we are losing around 43.3% of benzene in F101 vapor outlet stream. \n",
+ "\n",
+ "Let us try to minimize this cost such that:\n",
+ "- we are producing at least 0.18 mol/s of benzene as distillate i.e. our product stream\n",
+ "- purity of benzene i.e. the mole fraction of benzene in the distillate is at least 99%\n",
+ "- restricting the benzene loss in F101 vapor outlet to less than 20%\n",
+ "\n",
+ "For this problem, our decision variables are as follows:\n",
+ "- H101 outlet temperature\n",
+ "- R101 outlet temperature\n",
+ "- F101 outlet temperature\n",
+ "- H102 outlet temperature\n",
+ "- Condenser pressure\n",
+ "- reflux ratio\n",
+ "- boilup ratio\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let us declare our objective function for this problem. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 60,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.fs.objective = Objective(expr=m.fs.operating_cost + m.fs.capital_cost)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now, we need to unfix the decision variables as we had solved a square problem (degrees of freedom = 0) until now. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 61,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.fs.H101.outlet.temperature.unfix()\n",
+ "m.fs.R101.conversion.unfix()\n",
+ "m.fs.F101.vap_outlet.temperature.unfix()\n",
+ "m.fs.D101.condenser.condenser_pressure.unfix()\n",
+ "m.fs.D101.condenser.reflux_ratio.unfix()\n",
+ "m.fs.D101.reboiler.boilup_ratio.unfix()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "Let us now unfix the remaining variable: the temperature of the outlet from H102\n",
+ "\n",
+ "Use Shift+Enter to run the cell once you have typed in your code. \n",
+ "
\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 62,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Unfix the temperature of the outlet from H102"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Next, we need to set bounds on these decision variables to values shown below:\n",
+ "\n",
+ " - H101 outlet temperature [500, 600] K\n",
+ " - R101 outlet temperature [600, 900] K\n",
+ " - F101 outlet temperature [298, 450] K\n",
+ " - H102 outlet temperature [350, 400] K\n",
+ " - D101 condenser pressure [101325, 150000] Pa\n",
+ " - D101 reflux ratio [0.1, 5]\n",
+ " - D101 boilup ratio [0.1, 5]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 64,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Set bounds on the temperature of the outlet from H101\n",
+ "m.fs.H101.outlet.temperature[0].setlb(500)\n",
+ "m.fs.H101.outlet.temperature[0].setub(600)\n",
+ "\n",
+ "# Set bounds on the temperature of the outlet from R101\n",
+ "m.fs.R101.outlet.temperature[0].setlb(600)\n",
+ "m.fs.R101.outlet.temperature[0].setub(900)\n",
+ "\n",
+ "# Set bounds on the volume of the reactor R101\n",
+ "m.fs.R101.volume[0].setlb(0)\n",
+ "\n",
+ "# Set bounds on the temperature of the vapor outlet from F101\n",
+ "m.fs.F101.vap_outlet.temperature[0].setlb(298)\n",
+ "m.fs.F101.vap_outlet.temperature[0].setub(450.0)\n",
+ "\n",
+ "# Set bounds on the temperature of the outlet from H102\n",
+ "m.fs.H102.outlet.temperature[0].setlb(350)\n",
+ "m.fs.H102.outlet.temperature[0].setub(400)\n",
+ "\n",
+ "# Set bounds on the pressure inside the condenser\n",
+ "m.fs.D101.condenser.condenser_pressure.setlb(101325)\n",
+ "m.fs.D101.condenser.condenser_pressure.setub(150000)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "Now, set the bounds for the D101 reflux ratio and boilup ratio.\n",
+ "\n",
+ "Use Shift+Enter to run the cell once you have typed in your code. \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 65,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Set bounds on the reflux ratio\n",
+ "\n",
+ "\n",
+ "# Todo: Set bounds on the boilup ratio"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now, the only things left to define are our constraints on overhead loss in F101, distillate flowrate and its purity. Let us first look at defining a constraint for the overhead loss in F101 where we are restricting the benzene leaving the vapor stream to less than 20 % of the benzene available in the reactor outlet. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 67,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Ensure that the overhead loss of benzene from F101 <= 20%\n",
+ "m.fs.overhead_loss = Constraint(\n",
+ " expr=m.fs.F101.vap_outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]\n",
+ " <= 0.20 * m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "Now, add the constraint such that we are producing at least 0.18 mol/s of benzene in the product stream which is the distillate of D101. Let us name this constraint as m.fs.product_flow. \n",
+ "\n",
+ "Use Shift+Enter to run the cell once you have typed in your code. \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 68,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Add minimum product flow constraint"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let us add the final constraint on product purity or the mole fraction of benzene in the distillate such that it is at least greater than 99%. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 70,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.fs.product_purity = Constraint(\n",
+ " expr=m.fs.D101.condenser.distillate.mole_frac_comp[0, \"benzene\"] >= 0.99\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "We have now defined the optimization problem and we are now ready to solve this problem. \n",
+ "\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 71,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "WARNING: model contains export suffix 'scaling_factor' that contains 3\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 150 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "Ipopt 3.13.2: nlp_scaling_method=gradient-based\n",
+ "tol=1e-06\n",
+ "max_iter=200\n",
+ "\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...: 4073\n",
+ "Number of nonzeros in inequality constraint Jacobian.: 6\n",
+ "Number of nonzeros in Lagrangian Hessian.............: 2391\n",
+ "\n",
+ "Total number of variables............................: 1176\n",
+ " variables with only lower bounds: 113\n",
+ " variables with lower and upper bounds: 372\n",
+ " variables with only upper bounds: 0\n",
+ "Total number of equality constraints.................: 1169\n",
+ "Total number of inequality constraints...............: 3\n",
+ " inequality constraints with only lower bounds: 2\n",
+ " inequality constraints with lower and upper bounds: 0\n",
+ " inequality constraints with only upper bounds: 1\n",
+ "\n",
+ "iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n",
+ " 0 4.4230147e+05 2.99e+05 9.90e+01 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
+ " 1 4.3753585e+05 2.91e+05 1.28e+02 -1.0 3.09e+06 - 3.58e-01 2.40e-02f 1\n",
+ " 2 4.3545100e+05 2.78e+05 1.55e+02 -1.0 1.78e+06 - 3.31e-01 4.74e-02h 1\n",
+ " 3 4.2822311e+05 2.20e+05 4.50e+02 -1.0 2.99e+06 - 2.95e-02 1.35e-01h 1\n",
+ " 4 4.2249096e+05 1.45e+05 1.43e+03 -1.0 7.01e+06 - 5.14e-01 2.03e-01h 1\n",
+ " 5 4.2194364e+05 8.17e+04 1.70e+04 -1.0 6.06e+06 - 5.97e-01 4.28e-01h 1\n",
+ " 6 4.2602765e+05 4.55e+04 1.10e+06 -1.0 4.32e+06 - 9.26e-01 5.07e-01h 1\n",
+ " 7 4.3776643e+05 2.03e+04 6.44e+09 -1.0 2.42e+06 - 9.90e-01 9.47e-01h 1\n",
+ " 8 4.3846260e+05 1.92e+04 6.05e+09 -1.0 4.42e+05 - 5.40e-01 5.74e-02h 1\n",
+ " 9 4.4529853e+05 4.05e+04 4.66e+10 -1.0 2.47e+05 - 9.96e-01 9.90e-01h 1\n",
+ "iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n",
+ " 10 4.4906283e+05 9.76e+03 1.10e+10 -1.0 1.12e+03 -4.0 1.26e-01 7.45e-01h 1\n",
+ " 11 4.5079086e+05 1.19e+03 1.54e+09 -1.0 5.63e+02 -4.5 3.77e-01 1.00e+00h 1\n",
+ " 12 4.5024224e+05 2.66e+00 3.67e+06 -1.0 6.61e+01 -5.0 1.00e+00 1.00e+00f 1\n",
+ " 13 4.4946170e+05 5.64e-01 9.29e+05 -1.0 1.81e+02 -5.4 1.00e+00 7.88e-01f 1\n",
+ " 14 4.4916780e+05 8.48e+00 1.62e+05 -1.0 2.83e+02 -5.9 1.00e+00 1.00e+00f 1\n",
+ " 15 4.4899127e+05 4.83e+00 9.07e+04 -1.0 1.01e+02 -6.4 1.00e+00 4.40e-01f 2\n",
+ " 16 4.4886718e+05 7.00e-01 4.61e+02 -1.0 2.35e+02 -6.9 1.00e+00 1.00e+00f 1\n",
+ " 17 4.4800159e+05 1.39e+02 4.52e+06 -3.8 1.17e+03 -7.3 9.79e-01 9.37e-01f 1\n",
+ " 18 4.4672196e+05 9.59e+02 1.22e+06 -3.8 4.55e+03 -7.8 1.00e+00 9.43e-01f 1\n",
+ " 19 4.4401667e+05 7.75e+03 1.55e+05 -3.8 1.08e+04 -8.3 1.00e+00 1.00e+00f 1\n",
+ "iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n",
+ " 20 4.4185035e+05 1.91e+04 1.36e+04 -3.8 1.33e+04 -8.8 1.00e+00 1.00e+00h 1\n",
+ " 21 4.4241001e+05 3.52e+03 5.96e+03 -3.8 2.94e+03 -9.2 1.00e+00 1.00e+00h 1\n",
+ " 22 4.4185237e+05 7.82e+00 2.91e+02 -3.8 7.13e+03 -9.7 2.39e-01 1.00e+00h 1\n",
+ " 23 4.4124091e+05 1.53e+01 3.11e+02 -3.8 4.82e+04 -10.2 8.59e-01 1.36e-01f 1\n",
+ " 24 4.4137379e+05 1.80e+00 2.91e+02 -3.8 1.41e+04 - 1.95e-01 1.00e+00h 1\n",
+ " 25 4.3862833e+05 1.70e+03 9.48e+04 -3.8 1.57e+07 - 1.29e-03 9.10e-02f 1\n",
+ " 26 4.3883308e+05 1.49e+03 8.46e+04 -3.8 1.02e+06 - 1.00e+00 1.35e-01h 1\n",
+ " 27 4.3885472e+05 2.18e+01 3.40e+03 -3.8 1.38e+05 - 1.00e+00 1.00e+00h 1\n",
+ " 28 4.3884160e+05 5.90e-02 6.38e+01 -3.8 8.66e+03 - 1.00e+00 1.00e+00h 1\n",
+ " 29 4.3884157e+05 6.48e-07 4.63e-04 -3.8 2.89e+01 - 1.00e+00 1.00e+00h 1\n",
+ "iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n",
+ " 30 4.3883990e+05 3.57e-01 2.38e+03 -5.7 8.19e+02 - 1.00e+00 1.00e+00f 1\n",
+ " 31 4.3883992e+05 3.50e-07 7.79e-06 -5.7 3.55e-01 - 1.00e+00 1.00e+00h 1\n",
+ " 32 4.3883990e+05 5.47e-05 3.63e-01 -8.0 1.01e+01 - 1.00e+00 1.00e+00h 1\n",
+ " 33 4.3883990e+05 2.24e-08 1.46e-07 -8.0 5.42e-05 - 1.00e+00 1.00e+00h 1\n",
+ "\n",
+ "Number of Iterations....: 33\n",
+ "\n",
+ " (scaled) (unscaled)\n",
+ "Objective...............: 4.3883989842628603e+02 4.3883989842628600e+05\n",
+ "Dual infeasibility......: 1.4600704448671754e-07 1.4600704448671753e-04\n",
+ "Constraint violation....: 2.9103830456733704e-11 2.2351741790771484e-08\n",
+ "Complementarity.........: 9.0909948039799681e-09 9.0909948039799686e-06\n",
+ "Overall NLP error.......: 9.0909948039799681e-09 1.4600704448671753e-04\n",
+ "\n",
+ "\n",
+ "Number of objective function evaluations = 35\n",
+ "Number of objective gradient evaluations = 34\n",
+ "Number of equality constraint evaluations = 35\n",
+ "Number of inequality constraint evaluations = 35\n",
+ "Number of equality constraint Jacobian evaluations = 34\n",
+ "Number of inequality constraint Jacobian evaluations = 34\n",
+ "Number of Lagrangian Hessian evaluations = 33\n",
+ "Total CPU secs in IPOPT (w/o function evaluations) = 0.164\n",
+ "Total CPU secs in NLP function evaluations = 0.020\n",
+ "\n",
+ "EXIT: Optimal Solution Found.\n"
+ ]
+ }
+ ],
+ "source": [
+ "results = solver.solve(m, tee=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Optimization Results\n",
+ "\n",
+ "Display the results and product specifications"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 73,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "total cost = $ 438839.898426286\n",
+ "operating cost = $ 408883.5314830889\n",
+ "capital cost = $ 29956.3669431971\n",
+ "\n",
+ "Distillate flowrate = 0.1799999900263989 mol/s\n",
+ "Benzene purity = 98.99999900049086 %\n",
+ "Residue flowrate = 0.1085161642426372 mol/s\n",
+ "Toluene purity = 15.676178086213548 %\n",
+ "\n",
+ "Conversion = 93.38705916369427 %\n",
+ "\n",
+ "Overhead benzene loss in F101 = 17.34061793115618 %\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(\"total cost = $\", value(m.fs.capital_cost) + value(m.fs.operating_cost))\n",
+ "print(\"operating cost = $\", value(m.fs.operating_cost))\n",
+ "print(\"capital cost = $\", value(m.fs.capital_cost))\n",
+ "print()\n",
+ "print(\n",
+ " \"Distillate flowrate = \",\n",
+ " value(m.fs.D101.condenser.distillate.flow_mol[0]()),\n",
+ " \"mol/s\",\n",
+ ")\n",
+ "print(\n",
+ " \"Benzene purity = \",\n",
+ " 100 * value(m.fs.D101.condenser.distillate.mole_frac_comp[0, \"benzene\"]),\n",
+ " \"%\",\n",
+ ")\n",
+ "print(\"Residue flowrate = \", value(m.fs.D101.reboiler.bottoms.flow_mol[0]()), \"mol/s\")\n",
+ "print(\n",
+ " \"Toluene purity = \",\n",
+ " 100 * value(m.fs.D101.reboiler.bottoms.mole_frac_comp[0, \"toluene\"]),\n",
+ " \"%\",\n",
+ ")\n",
+ "print()\n",
+ "print(\"Conversion = \", 100 * value(m.fs.R101.conversion), \"%\")\n",
+ "print()\n",
+ "print(\n",
+ " \"Overhead benzene loss in F101 = \",\n",
+ " 100\n",
+ " * value(m.fs.F101.vap_outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"])\n",
+ " / value(m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]),\n",
+ " \"%\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Display optimal values for the decision variables"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 75,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Optimal Values\n",
+ "\n",
+ "H101 outlet temperature = 568.923204295196 K\n",
+ "\n",
+ "R101 outlet temperature = 790.3655425698853 K\n",
+ "\n",
+ "F101 outlet temperature = 298.0 K\n",
+ "\n",
+ "H102 outlet temperature = 368.7414339952852 K\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(\"Optimal Values\")\n",
+ "print()\n",
+ "\n",
+ "print(\"H101 outlet temperature = \", value(m.fs.H101.outlet.temperature[0]), \"K\")\n",
+ "\n",
+ "print()\n",
+ "print(\"R101 outlet temperature = \", value(m.fs.R101.outlet.temperature[0]), \"K\")\n",
+ "\n",
+ "print()\n",
+ "print(\"F101 outlet temperature = \", value(m.fs.F101.vap_outlet.temperature[0]), \"K\")\n",
+ "\n",
+ "print()\n",
+ "print(\"H102 outlet temperature = \", value(m.fs.H102.outlet.temperature[0]), \"K\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Key Takeaways\n",
+ "\n",
+ "Observe that the optimization was able to reduce the yearly operating cost from \\\\$427,593 to \\\\$408,342 (~4.5%). However, the amortized capital cost more than doubled from \\\\$14,704 to \\\\$29,927 due to the need to increase the conversion in the reactor (from 75% to 93%) to meet the production and purity constraints. \n",
+ "\n",
+ "Further, observe that the product flow rate and product purity are at their minimum values (0.18 mol/s and 99%, respectively). This is expected as increasing recovery would require more energy and cost to purify the product.\n",
+ "\n",
+ "\n",
+ "Finally, observe that the operating temperature of the flash (F101) is almost at its lower bound. This helps in minimizing the amount of benzene in the vapor stream leaving the flash."
+ ]
+ }
+ ],
+ "metadata": {
+ "celltoolbar": "Tags",
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
},
- "nbformat": 4,
- "nbformat_minor": 3
+ "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.10.13"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 3
}
\ No newline at end of file
diff --git a/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_distillation_solution.ipynb b/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_distillation_solution.ipynb
index ecabdf9c..86d59dd3 100644
--- a/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_distillation_solution.ipynb
+++ b/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_distillation_solution.ipynb
@@ -1,1791 +1,3024 @@
{
- "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": [
- "\n",
- "# HDA Flowsheet Simulation and Optimization\n",
- "Maintainer: Brandon Paul \n",
- "Author: Brandon Paul \n",
- "Updated: 2023-06-01 \n",
- "\n",
- "\n",
- "## Note\n",
- "\n",
- "This tutorial will be similar to the HDA flowsheet tutorial in the Tutorials section, except that we use a distillation column instead of a second flash (F102) to produce benzene and toluene products.\n",
- "\n",
- "\n",
- "## Learning outcomes\n",
- "\n",
- "\n",
- "- Construct a steady-state flowsheet using the IDAES unit model library\n",
- "- Connecting unit models in a flowsheet using Arcs\n",
- "- Using the SequentialDecomposition tool to initialize a flowsheet with recycle\n",
- "- Fomulate and solve an optimization problem\n",
- " - Defining an objective function\n",
- " - Setting variable bounds\n",
- " - Adding additional constraints \n",
- "\n",
- "\n",
- "## Problem Statement\n",
- "\n",
- "Hydrodealkylation is a chemical reaction that often involves reacting\n",
- "an aromatic hydrocarbon in the presence of hydrogen gas to form a\n",
- "simpler aromatic hydrocarbon devoid of functional groups. In this\n",
- "example, toluene will be reacted with hydrogen gas at high temperatures\n",
- " to form benzene via the following reaction:\n",
- "\n",
- "**C6H5CH3 + H2 \u2192 C6H6 + CH4**\n",
- "\n",
- "\n",
- "This reaction is often accompanied by an equilibrium side reaction\n",
- "which forms diphenyl, which we will neglect for this example.\n",
- "\n",
- "This example is based on the 1967 AIChE Student Contest problem as\n",
- "present by Douglas, J.M., Chemical Design of Chemical Processes, 1988,\n",
- "McGraw-Hill.\n",
- "\n",
- "The flowsheet that we will be using for this module is shown below with the stream conditions. We will be processing toluene and hydrogen to produce at least 370 TPY of benzene. As shown in the flowsheet, we use a flash tank, F101, to separate out the non-condensibles, and a distillation column, D101, to further separate the benzene-toluene mixture to improve the benzene purity. The non-condensibles separated out in F101 will be partially recycled back to M101 and the rest will be purged. We will assume ideal gas behavior for this flowsheet. The properties required for this module are defined in\n",
- "\n",
- "- `hda_ideal_VLE.py`\n",
- "- `idaes.models.properties.activity_coeff_models.BTX_activity_coeff_VLE`\n",
- "- `hda_reaction.py`\n",
- "\n",
- "We will be using two thermodynamic packages: one (first in the list above) containing all four components (i.e., toluene, hydrogen, benzene, and methane) and the other (second in the list above) containing benzene and toluene only. The latter is needed to simplify the VLE calculations in the distillation column model. \n",
- "\n",
- "![](HDA_flowsheet_distillation.png)\n",
- "\n",
- ""
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Translator block\n",
- "\n",
- "Benzene and toluene are separated by distillation, so the process involves phase equilibrium and two-phase flow conditions. However, the presence of hydrogen and methane complicates the calculations. This is because, hydrogen and methane are non-condensable under all conditions of interest; ergo, a vapor phase will always be present, and the mixture bubble point is extremely low. To simplify the phase equilibrium calculations, hydrogen and methane will be considered completely as non-condensable and insoluble in the liquid outlet from the flash F101.\n",
- "\n",
- "Since no hydrogen and methane will be present in the unit operations following the flash, a different component list can be used to simplify the property calculations. IDAES supports the definition of multiple property packages within a single flowsheet via `Translator` blocks. `Translator` blocks convert between different property calculations, component lists, and equations of state. "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Importing required pyomo and idaes components\n",
- "\n",
- "\n",
- "To construct a flowsheet, we will need several components from the pyomo and idaes package. Let us first import the following components from Pyomo:\n",
- "- Constraint (to write constraints)\n",
- "- Var (to declare variables)\n",
- "- ConcreteModel (to create the concrete model object)\n",
- "- Expression (to evaluate values as a function of variables defined in the model)\n",
- "- Objective (to define an objective function for optimization)\n",
- "- SolverFactory (to solve the problem)\n",
- "- TransformationFactory (to apply certain transformations)\n",
- "- Arc (to connect two unit models)\n",
- "- SequentialDecomposition (to initialize the flowsheet in a sequential mode)\n",
- "\n",
- "For further details on these components, please refer to the pyomo documentation: https://pyomo.readthedocs.io/en/stable/\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "from pyomo.environ import (\n",
- " Constraint,\n",
- " Var,\n",
- " ConcreteModel,\n",
- " Expression,\n",
- " Objective,\n",
- " TransformationFactory,\n",
- " value,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "Import `Arc` and `SequentialDecomposition` tools from `pyomo.network`\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Import the above mentioned tools from pyomo.network"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Import the above mentioned tools from pyomo.network\n",
- "from pyomo.network import Arc, SequentialDecomposition"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "From IDAES, we will be needing the FlowsheetBlock and the following unit models:\n",
- "- Mixer\n",
- "- Heater\n",
- "- CSTR\n",
- "- Flash\n",
- "- Separator (splitter) \n",
- "- PressureChanger\n",
- "- Translator (to switch from one property package to another)\n",
- "- TrayColumn (distillation column)\n",
- "- CondenserType (Type of the overhead condenser: complete or partial)\n",
- "- TemperatureSpec (Temperature specification inside the condenser)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [],
- "source": [
- "from idaes.core import FlowsheetBlock"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {},
- "outputs": [],
- "source": [
- "from idaes.models.unit_models import (\n",
- " PressureChanger,\n",
- " Mixer,\n",
- " Separator as Splitter,\n",
- " Heater,\n",
- " CSTR,\n",
- " Flash,\n",
- " Translator,\n",
- ")\n",
- "\n",
- "from idaes.models_extra.column_models import TrayColumn\n",
- "from idaes.models_extra.column_models.condenser import CondenserType, TemperatureSpec"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We will also be needing some utility tools to put together the flowsheet and calculate the degrees of freedom. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Utility tools to put together the flowsheet and calculate the degrees of freedom\n",
- "from idaes.models.unit_models.pressure_changer import ThermodynamicAssumption\n",
- "from idaes.core.util.model_statistics import degrees_of_freedom\n",
- "from idaes.core.util.initialization import propagate_state\n",
- "from idaes.core.solvers import get_solver\n",
- "import idaes.core.util.scaling as iscale\n",
- "\n",
- "# Import idaes logger to set output levels\n",
- "import idaes.logger as idaeslog"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Importing required thermo and reaction packages\n",
- "\n",
- "Finally, we import the thermophysical (`ideal_VLE.py` and `BTXParameterBlock`) packages and reaction package (`reaction.py`) for the HDA process. We have created custom thermophysical packages that assume ideal gas behavior with support for VLE. The reaction package consists of the stochiometric coefficients for the reaction, heat of reaction, and kinetic information (Arrhenius constant and activation energy). "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "metadata": {},
- "outputs": [],
- "source": [
- "from idaes_examples.mod.hda import hda_reaction as reaction_props\n",
- "from idaes.models.properties.activity_coeff_models.BTX_activity_coeff_VLE import (\n",
- " BTXParameterBlock,\n",
- ")\n",
- "\n",
- "from idaes_examples.mod.hda.hda_ideal_VLE import HDAParameterBlock"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Constructing the Flowsheet\n",
- "\n",
- "We have now imported all the components, unit models, and property modules we need to construct a flowsheet. Let us create a ConcreteModel and add the flowsheet block to it. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Create a Pyomo Concrete Model to contain the problem\n",
- "m = ConcreteModel()\n",
- "\n",
- "# Add a steady state flowsheet block to the model\n",
- "m.fs = FlowsheetBlock(dynamic=False)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We will now add the thermophysical and reaction packages to the flowsheet."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Property package for benzene, toluene, hydrogen, methane mixture\n",
- "m.fs.BTHM_params = HDAParameterBlock()\n",
- "\n",
- "# Property package for the benzene-toluene mixture\n",
- "m.fs.BT_params = BTXParameterBlock(\n",
- " valid_phase=(\"Liq\", \"Vap\"), activity_coeff_model=\"Ideal\"\n",
- ")\n",
- "\n",
- "# Reaction package for the HDA reaction\n",
- "m.fs.reaction_params = reaction_props.HDAReactionParameterBlock(\n",
- " property_package=m.fs.BTHM_params\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Adding Unit Models\n",
- "\n",
- "Let us start adding the unit models we have imported to the flowsheet. Here, we are adding the Mixer (assigned a name M101) and a Heater (assigned a name H101). Note that, all unit models need to be given a property package argument. In addition, the Mixer unit model needs a `list` consisting of the inlets (toluene feed, hydrogen feed and vapor recycle streams in this case). "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 11,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Adding the mixer M101 to the flowsheet\n",
- "m.fs.M101 = Mixer(\n",
- " property_package=m.fs.BTHM_params,\n",
- " inlet_list=[\"toluene_feed\", \"hydrogen_feed\", \"vapor_recycle\"],\n",
- ")\n",
- "\n",
- "# Adding the heater H101 to the flowsheet\n",
- "m.fs.H101 = Heater(property_package=m.fs.BTHM_params, has_phase_equilibrium=True)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "
Inline Exercise:\n",
- "Let us now add the CSTR (assign the name R101) and pass the following arguments:\n",
- "
\n",
- " - \"property_package\": m.fs.BTHM_params
\n",
- " - \"reaction_package\": m.fs.reaction_params
\n",
- " - \"has_heat_of_reaction\": True
\n",
- " - \"has_heat_transfer\": True
\n",
- "
\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 12,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Add reactor with the specifications above"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 13,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Add reactor with the specifications above\n",
- "m.fs.R101 = CSTR(\n",
- " property_package=m.fs.BTHM_params,\n",
- " reaction_package=m.fs.reaction_params,\n",
- " has_heat_of_reaction=True,\n",
- " has_heat_transfer=True,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Let us now add the Flash (assign the name F101), Splitter (assign the name S101) and PressureChanger (assign the name C101)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 14,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Adding the flash tank F101 to the flowsheet\n",
- "m.fs.F101 = Flash(\n",
- " property_package=m.fs.BTHM_params, has_heat_transfer=True, has_pressure_change=True\n",
- ")\n",
- "\n",
- "# Adding the splitter S101 to the flowsheet\n",
- "m.fs.S101 = Splitter(\n",
- " property_package=m.fs.BTHM_params, outlet_list=[\"purge\", \"recycle\"]\n",
- ")\n",
- "\n",
- "# Adding the compressor C101 to the flowsheet\n",
- "m.fs.C101 = PressureChanger(\n",
- " property_package=m.fs.BTHM_params,\n",
- " compressor=True,\n",
- " thermodynamic_assumption=ThermodynamicAssumption.isothermal,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Remark\n",
- "\n",
- "Currently, the `SequentialDecomposition()` tool, which we will later be using to initialize the flowsheet, does not support the distillation column model. Thus, we will first simulate the flowsheet without the distillation column. After it converges, we will then add the distillation column, initialize it, and simulate the entire flowsheet."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "As mentioned above, we use the `m.fs.BTHM_params` package, which contains all the four species, for the reactor loop, and the simpler `m.fs.BT_params` for unit operations following the flash (i.e., heater H102 and the distillation column D101). We define a `Translator` block to link the source property package and the package it is to be translated to in the following manner:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 15,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Add translator block to convert between property packages\n",
- "m.fs.translator = Translator(\n",
- " inlet_property_package=m.fs.BTHM_params, outlet_property_package=m.fs.BT_params\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Translator block constraints\n",
- "\n",
- "The `Translator` block needs to know how to translate between the two property packages. This must be custom coded for each application because of the generality of the IDAES framework.\n",
- "\n",
- "For this process, five constraints are required based on the state variables used in the outgoing process.\n",
- "\n",
- "- Since we assumed that only benzene and toluene are present in the liquid phase, the total molar flowrate must be the sum of molar flowrates of benzene and toluene, respectively.\n",
- "- Temperature of the inlet and outlet streams must be the same.\n",
- "- Pressure of the inlet and outgoing streams must be the same\n",
- "- The mole fraction of benzene in the outgoing stream is the ratio of the molar flowrate of liquid benzene in the inlet to the sum of molar flowrates of liquid benzene and toluene in the inlet.\n",
- "- The mole fraction of toluene in the outgoing stream is the ratio of the molar flowrate of liquid toluene in the inlet to the sum of molar flowrates of liquid benzene and toluene in the inlet."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 16,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Add constraint: Total flow = benzene flow + toluene flow (molar)\n",
- "m.fs.translator.eq_total_flow = Constraint(\n",
- " expr=m.fs.translator.outlet.flow_mol[0]\n",
- " == m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
- " + m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
- ")\n",
- "\n",
- "# Add constraint: Outlet temperature = Inlet temperature\n",
- "m.fs.translator.eq_temperature = Constraint(\n",
- " expr=m.fs.translator.outlet.temperature[0] == m.fs.translator.inlet.temperature[0]\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "In the above, note that the variable flow_mol_phase_comp has the index - [time, phase, component]. As this is a steady-state flowsheet, the time index by default is 0. The valid phases are [\"Liq\", \"Vap\"]. Similarly the valid component list is [\"benzene\", \"toluene\", \"hydrogen\", \"methane\"]."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "Add the constraint to ensure that the outlet pressure is the same as the inlet pressure\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 17,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Add constraint: Outlet pressure = Inlet pressure"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 18,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Add constraint: Outlet pressure = Inlet pressure\n",
- "m.fs.translator.eq_pressure = Constraint(\n",
- " expr=m.fs.translator.outlet.pressure[0] == m.fs.translator.inlet.pressure[0]\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 19,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Remaining constraints on the translator block\n",
- "\n",
- "# Add constraint: Benzene mole fraction definition\n",
- "m.fs.translator.eq_mole_frac_benzene = Constraint(\n",
- " expr=m.fs.translator.outlet.mole_frac_comp[0, \"benzene\"]\n",
- " == m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
- " / (\n",
- " m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
- " + m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
- " )\n",
- ")\n",
- "\n",
- "# Add constraint: Toluene mole fraction definition\n",
- "m.fs.translator.eq_mole_frac_toluene = Constraint(\n",
- " expr=m.fs.translator.outlet.mole_frac_comp[0, \"toluene\"]\n",
- " == m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
- " / (\n",
- " m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
- " + m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
- " )\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "Finally, let us add the Heater H102 in the same way as H101 but pass the m.fs.BT_params thermodynamic package. We will add the distillation column after converging the flowsheet.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 20,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Add the Heater H102 to the flowsheet"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 21,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Add the Heater H102 to the flowsheet\n",
- "m.fs.H102 = Heater(\n",
- " property_package=m.fs.BT_params,\n",
- " has_pressure_change=True,\n",
- " has_phase_equilibrium=True,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Connecting Unit Models using Arcs\n",
- "\n",
- "We have now added the initial set of unit models to the flowsheet. However, we have not yet specifed how the units are connected. To do this, we will be using the `Arc` which is a pyomo component that takes in two arguments: `source` and `destination`. Let us connect the outlet of the mixer (M101) to the inlet of the heater (H101). "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 22,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.s03 = Arc(source=m.fs.M101.outlet, destination=m.fs.H101.inlet)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "![](HDA_flowsheet_distillation.png) \n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Now, connect the H101 outlet to the R101 inlet using the cell above as a guide. \n",
- "
\n",
- "\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 23,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Connect the H101 outlet to R101 inlet"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 24,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Connect the H101 outlet to R101 inlet\n",
- "m.fs.s04 = Arc(source=m.fs.H101.outlet, destination=m.fs.R101.inlet)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We will now be connecting the rest of the units as shown below. Notice how the outlet names are different for the flash tank as it has a vapor and a liquid outlet. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 25,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.s05 = Arc(source=m.fs.R101.outlet, destination=m.fs.F101.inlet)\n",
- "m.fs.s06 = Arc(source=m.fs.F101.vap_outlet, destination=m.fs.S101.inlet)\n",
- "m.fs.s08 = Arc(source=m.fs.S101.recycle, destination=m.fs.C101.inlet)\n",
- "m.fs.s09 = Arc(source=m.fs.C101.outlet, destination=m.fs.M101.vapor_recycle)\n",
- "m.fs.s10a = Arc(source=m.fs.F101.liq_outlet, destination=m.fs.translator.inlet)\n",
- "m.fs.s10b = Arc(source=m.fs.translator.outlet, destination=m.fs.H102.inlet)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We have now connected the unit model block using the arcs. However, each of these arcs link to ports on the two unit models that are connected. In this case, the ports consist of the state variables that need to be linked between the unit models. Pyomo provides a convenient method to write these equality constraints for us between two ports and this is done as follows:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 26,
- "metadata": {},
- "outputs": [],
- "source": [
- "TransformationFactory(\"network.expand_arcs\").apply_to(m)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Appending additional constraints to the model\n",
- "\n",
- "Now, we will see how we can add additional constraints to the model using `Constraint` from Pyomo.\n",
- "\n",
- "Consider the reactor R101. By default, the conversion of a component is not calculated when we simulate the flowsheet. If we are interested either in specifying or constraining the conversion value, we can add the following constraint to calculate the conversion:\n",
- "$$ \\text{Conversion of toluene} = \\frac{\\text{molar flow of toluene in the inlet} - \\text{molar flow of toluene in the outlet}}{\\text{molar flow of toluene in the inlet}} $$ \n",
- "\n",
- "We add the constraint to the model as shown below."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 27,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Define the conversion variables using 'Var'\n",
- "m.fs.R101.conversion = Var(initialize=0.75, bounds=(0, 1))\n",
- "\n",
- "# Append the constraint to the model\n",
- "m.fs.R101.conv_constraint = Constraint(\n",
- " expr=m.fs.R101.conversion * m.fs.R101.inlet.flow_mol_phase_comp[0, \"Vap\", \"toluene\"]\n",
- " == (\n",
- " m.fs.R101.inlet.flow_mol_phase_comp[0, \"Vap\", \"toluene\"]\n",
- " - m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"toluene\"]\n",
- " )\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Fixing feed conditions and Initializing the flowsheet\n",
- "\n",
- "Let us first check how many degrees of freedom exist for this flowsheet using the `degrees_of_freedom` tool we imported earlier. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 28,
- "metadata": {},
- "outputs": [],
- "source": [
- "print(degrees_of_freedom(m))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We will now be fixing the toluene feed stream to the conditions shown in the flowsheet above. Please note that though this is a pure toluene feed, the remaining components are still assigned a very small non-zero value to help with convergence and initializing. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 30,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"benzene\"].fix(1e-5)\n",
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"toluene\"].fix(1e-5)\n",
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"hydrogen\"].fix(1e-5)\n",
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"methane\"].fix(1e-5)\n",
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"benzene\"].fix(1e-5)\n",
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"toluene\"].fix(0.30)\n",
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"hydrogen\"].fix(1e-5)\n",
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"methane\"].fix(1e-5)\n",
- "m.fs.M101.toluene_feed.temperature.fix(303.2)\n",
- "m.fs.M101.toluene_feed.pressure.fix(350000)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Similarly, let us fix the hydrogen feed to the following conditions in the next cell:\n",
- " \n",
- " - FH2 = 0.30 mol/s
\n",
- " - FCH4 = 0.02 mol/s
\n",
- " - Remaining components = 1e-5 mol/s
\n",
- " - T = 303.2 K
\n",
- " - P = 350000 Pa
\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 31,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"benzene\"].fix(1e-5)\n",
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"toluene\"].fix(1e-5)\n",
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"hydrogen\"].fix(0.30)\n",
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"methane\"].fix(0.02)\n",
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"benzene\"].fix(1e-5)\n",
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"toluene\"].fix(1e-5)\n",
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"hydrogen\"].fix(1e-5)\n",
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"methane\"].fix(1e-5)\n",
- "m.fs.M101.hydrogen_feed.temperature.fix(303.2)\n",
- "m.fs.M101.hydrogen_feed.pressure.fix(350000)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Fixing unit model specifications\n",
- "\n",
- "Now that we have fixed our inlet feed conditions, we will now be fixing the operating conditions for the unit models in the flowsheet. Let us set the H101 outlet temperature to 600 K. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 32,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Fix the temperature of the outlet from the heater H101\n",
- "m.fs.H101.outlet.temperature.fix(600)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "
Inline Exercise:\n",
- "Set the conditions for the reactor R101 to the following conditions:\n",
- "
\n",
- " - `conversion` = 0.75
\n",
- " - `heat_duty` = 0
\n",
- "
\n",
- "\n",
- "Use Shift+Enter to run the cell once you have typed in your code. \n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 33,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Fix the 'conversion' of the reactor R101\n",
- "\n",
- "\n",
- "# Todo: Fix the 'heat_duty' of the reactor R101"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 34,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Fix the 'conversion' of the reactor R101\n",
- "m.fs.R101.conversion.fix(0.75)\n",
- "\n",
- "# Todo: Fix the 'heat_duty' of the reactor R101\n",
- "m.fs.R101.heat_duty.fix(0)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The Flash conditions for F101 can be set as follows. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 35,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Fix the temperature of the vapor outlet from F101\n",
- "m.fs.F101.vap_outlet.temperature.fix(325.0)\n",
- "\n",
- "# Fix the pressure drop in the flash F101\n",
- "m.fs.F101.deltaP.fix(0)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Let us fix the split fraction of the purge stream from the splitter S101 and the outlet pressure from the compressor C101"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 36,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Fix the split fraction of the 'purge' stream from S101\n",
- "m.fs.S101.split_fraction[0, \"purge\"].fix(0.2)\n",
- "\n",
- "# Fix the pressure of the outlet from the compressor C101\n",
- "m.fs.C101.outlet.pressure.fix(350000)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Finally, let us fix the temperature of the outlet from H102 and the pressure drop in H102 as the following"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 37,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Fix the temperature of the outlet from the heater H102\n",
- "m.fs.H102.outlet.temperature.fix(375)\n",
- "\n",
- "# Fix the pressure drop in the heater H102\n",
- "m.fs.H102.deltaP.fix(-200000)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "To avoid convergence issues associated with poorly scaled variables and/or constraints, we scale the variables and constraints corresponding to the heaters H101 and H102, flash F101 and the reactor R101. Scaling factors for the flow rates, temperature, pressure, etc. have been defined in the property package: `ideal_VLE.py` file. Here, we set scaling factors only for the heat duty of the heater, the reaction extent, heat duty and volume of the reactor."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 38,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Set scaling factors for heat duty, reaction extent and volume\n",
- "iscale.set_scaling_factor(m.fs.H101.control_volume.heat, 1e-2)\n",
- "iscale.set_scaling_factor(m.fs.R101.control_volume.heat, 1e-2)\n",
- "iscale.set_scaling_factor(m.fs.R101.control_volume.rate_reaction_extent, 1)\n",
- "iscale.set_scaling_factor(m.fs.R101.control_volume.volume, 1)\n",
- "iscale.set_scaling_factor(m.fs.F101.control_volume.heat, 1e-2)\n",
- "iscale.set_scaling_factor(m.fs.H102.control_volume.heat, 1e-2)\n",
- "\n",
- "# Set the scaling factors for the remaining variables and all constraints\n",
- "iscale.calculate_scaling_factors(m.fs.H101)\n",
- "iscale.calculate_scaling_factors(m.fs.R101)\n",
- "iscale.calculate_scaling_factors(m.fs.F101)\n",
- "iscale.calculate_scaling_factors(m.fs.H102)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "We have now defined all the feed conditions and the inputs required for the unit models. The system should now have 0 degrees of freedom i.e. should be a square problem. Please check that the degrees of freedom is 0. \n",
- "\n",
- "Use Shift+Enter to run the cell once you have typed in your code. \n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 39,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Check the degrees of freedom"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 40,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Check the degrees of freedom\n",
- "print(degrees_of_freedom(m))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Initialization\n",
- "\n",
- "This subsection will demonstrate how to use the built-in sequential decomposition tool to initialize our flowsheet.\n",
- "\n",
- "Let us first create an object for the `SequentialDecomposition` and specify our options for this. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 42,
- "metadata": {},
- "outputs": [],
- "source": [
- "seq = SequentialDecomposition()\n",
- "seq.options.select_tear_method = \"heuristic\"\n",
- "seq.options.tear_method = \"Wegstein\"\n",
- "seq.options.iterLim = 3\n",
- "\n",
- "# Using the SD tool\n",
- "G = seq.create_graph(m)\n",
- "heuristic_tear_set = seq.tear_set_arcs(G, method=\"heuristic\")\n",
- "order = seq.calculation_order(G)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Which is the tear stream? Display tear set and order"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 43,
- "metadata": {},
- "outputs": [],
- "source": [
- "for o in heuristic_tear_set:\n",
- " print(o.name)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "What sequence did the SD tool determine to solve this flowsheet with the least number of tears? "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 44,
- "metadata": {},
- "outputs": [],
- "source": [
- "for o in order:\n",
- " print(o[0].name)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The SequentialDecomposition tool has determined that the tear stream is the mixer outlet (s03 in the Figure above). We will need to provide a reasonable guess for this.\n",
- "\n",
- "For the initial guess, we assume that the flowrate of the recycle stream (s09) is zero. Consequently, the flow rate of the stream s03 is simply the sum of the flowrates of the toluene feed and hydrogen feed streams. Further, since the temperature and the pressure of both the toluene and hydrogen feed streams are the same, we specify their values as the initial guess for the temperature and pressure of the stream s03."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 45,
- "metadata": {},
- "outputs": [],
- "source": [
- "tear_guesses = {\n",
- " \"flow_mol_phase_comp\": {\n",
- " (0, \"Vap\", \"benzene\"): 1e-5,\n",
- " (0, \"Vap\", \"toluene\"): 1e-5,\n",
- " (0, \"Vap\", \"hydrogen\"): 0.30,\n",
- " (0, \"Vap\", \"methane\"): 0.02,\n",
- " (0, \"Liq\", \"benzene\"): 1e-5,\n",
- " (0, \"Liq\", \"toluene\"): 0.30,\n",
- " (0, \"Liq\", \"hydrogen\"): 1e-5,\n",
- " (0, \"Liq\", \"methane\"): 1e-5,\n",
- " },\n",
- " \"temperature\": {0: 303},\n",
- " \"pressure\": {0: 350000},\n",
- "}\n",
- "\n",
- "# Pass the tear_guess to the SD tool\n",
- "seq.set_guesses_for(m.fs.H101.inlet, tear_guesses)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Next, we need to tell the tool how to initialize a particular unit. We will be writing a python function which takes in a \"unit\" and calls the initialize method on that unit."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 46,
- "metadata": {},
- "outputs": [],
- "source": [
- "def function(unit):\n",
- " unit.initialize(outlvl=idaeslog.INFO)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We are now ready to initialize our flowsheet in a sequential mode. Note that we specifically set the iteration limit to be 3 as we are trying to use this tool only to get a good set of initial values such that IPOPT can then take over and solve this flowsheet for us. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 47,
- "metadata": {
- "scrolled": false
- },
- "outputs": [],
- "source": [
- "seq.run(m, function)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "We have now initialized the flowsheet. Let us run the flowsheet in a simulation mode to look at the results. \n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 48,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Create the solver object\n",
- "solver = get_solver()\n",
- "\n",
- "# Solve the model\n",
- "results = solver.solve(m, tee=True)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Add distillation column \n",
- "\n",
- "As mentioned earlier, the `SequentialDecomposition` tool currently does not support the distillation column model. Thus, we have not included the distillation column in the flowsheet. Now that we have a converged flowsheet, we will add the distillation column and simulate the entire flowsheet. \n",
- "\n",
- "In the following, we will\n",
- "- Add the distillation column \n",
- "- Connect it to the heater \n",
- "- Add the necessary equality constraints\n",
- "- Propogate the state variable information from the outlet of the heater to the inlet of the distillation column \n",
- "- Fix the degrees of freedom of the distillation block (reflux ratio, boilup ratio, and condenser pressure)\n",
- "- Scale the control volume heat variables to help convergence\n",
- "- Initialize the distillation block.\n",
- "\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 50,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Add distillation column to the flowsheet\n",
- "m.fs.D101 = TrayColumn(\n",
- " number_of_trays=10,\n",
- " feed_tray_location=5,\n",
- " condenser_type=CondenserType.totalCondenser,\n",
- " condenser_temperature_spec=TemperatureSpec.atBubblePoint,\n",
- " property_package=m.fs.BT_params,\n",
- ")\n",
- "\n",
- "# Connect the outlet from the heater H102 to the distillation column\n",
- "m.fs.s11 = Arc(source=m.fs.H102.outlet, destination=m.fs.D101.feed)\n",
- "\n",
- "# Add the necessary equality constraints\n",
- "TransformationFactory(\"network.expand_arcs\").apply_to(m)\n",
- "\n",
- "# Propagate the state\n",
- "propagate_state(m.fs.s11)\n",
- "\n",
- "# Fix the reflux ratio, boilup ratio, and the condenser pressure\n",
- "m.fs.D101.condenser.reflux_ratio.fix(0.5)\n",
- "m.fs.D101.reboiler.boilup_ratio.fix(0.5)\n",
- "m.fs.D101.condenser.condenser_pressure.fix(150000)\n",
- "\n",
- "# set scaling factors\n",
- "# Set scaling factors for heat duty\n",
- "iscale.set_scaling_factor(m.fs.D101.condenser.control_volume.heat, 1e-2)\n",
- "iscale.set_scaling_factor(m.fs.D101.reboiler.control_volume.heat, 1e-2)\n",
- "\n",
- "# Set the scaling factors for the remaining variables and all constraints\n",
- "iscale.calculate_scaling_factors(m.fs.D101)\n",
- "\n",
- "# Initialize the distillation column\n",
- "m.fs.D101.initialize(outlvl=idaeslog.INFO)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Adding expressions to compute capital and operating costs\n",
- "\n",
- "In this section, we will add a few Expressions that allow us to evaluate the performance. Expressions provide a convenient way of calculating certain values that are a function of the variables defined in the model. For more details on Expressions, please refer to: https://pyomo.readthedocs.io/en/stable/pyomo_modeling_components/Expressions.html"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 51,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Expression to compute the total cooling cost\n",
- "m.fs.cooling_cost = Expression(\n",
- " expr=0.25e-7 * (-m.fs.F101.heat_duty[0])\n",
- " + 0.2e-7 * (-m.fs.D101.condenser.heat_duty[0])\n",
- ")\n",
- "\n",
- "# Expression to compute the total heating cost\n",
- "m.fs.heating_cost = Expression(\n",
- " expr=2.2e-7 * m.fs.H101.heat_duty[0]\n",
- " + 1.2e-7 * m.fs.H102.heat_duty[0]\n",
- " + 1.9e-7 * m.fs.D101.reboiler.heat_duty[0]\n",
- ")\n",
- "\n",
- "# Expression to compute the total operating cost\n",
- "m.fs.operating_cost = Expression(\n",
- " expr=(3600 * 24 * 365 * (m.fs.heating_cost + m.fs.cooling_cost))\n",
- ")\n",
- "\n",
- "# Expression to compute the total capital cost\n",
- "m.fs.capital_cost = Expression(expr=1e5 * m.fs.R101.volume[0])"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Solve the entire flowsheet"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 53,
- "metadata": {},
- "outputs": [],
- "source": [
- "solver.solve(m, tee=True)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Analyze the Results of the Square Problem\n",
- "\n",
- "How much is the total cost (operating cost + capital cost), operating cost, capital cost, benzene purity in the distillate from the distilation column, and conversion of toluene in the reactor?"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 55,
- "metadata": {},
- "outputs": [],
- "source": [
- "print(\"total cost = $\", value(m.fs.capital_cost) + value(m.fs.operating_cost))\n",
- "print(\"operating cost = $\", value(m.fs.operating_cost))\n",
- "print(\"capital cost = $\", value(m.fs.capital_cost))\n",
- "print()\n",
- "print(\n",
- " \"Distillate flowrate = \",\n",
- " value(m.fs.D101.condenser.distillate.flow_mol[0]()),\n",
- " \"mol/s\",\n",
- ")\n",
- "print(\n",
- " \"Benzene purity = \",\n",
- " 100 * value(m.fs.D101.condenser.distillate.mole_frac_comp[0, \"benzene\"]),\n",
- " \"%\",\n",
- ")\n",
- "print(\"Residue flowrate = \", value(m.fs.D101.reboiler.bottoms.flow_mol[0]()), \"mol/s\")\n",
- "print(\n",
- " \"Toluene purity = \",\n",
- " 100 * value(m.fs.D101.reboiler.bottoms.mole_frac_comp[0, \"toluene\"]),\n",
- " \"%\",\n",
- ")\n",
- "print()\n",
- "print(\"Conversion = \", 100 * value(m.fs.R101.conversion), \"%\")\n",
- "print()\n",
- "print(\n",
- " \"Overhead benzene loss in F101 = \",\n",
- " 100\n",
- " * value(m.fs.F101.vap_outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"])\n",
- " / value(m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]),\n",
- " \"%\",\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Get the state of the streams entering and leaving the reactor R101"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 57,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.R101.report()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Get the state of the streams entering and leaving the reactor R101"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 58,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.F101.report()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Next, let's look at how much benzene we are losing with the light gases out of F101. IDAES has tools for creating stream tables based on the `Arcs` and/or `Ports` in a flowsheet. Let us create and print a simple stream table showing the stream leaving the reactor and the vapor stream from F101.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "How much benzene are we losing in the F101 vapor outlet stream?\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 59,
- "metadata": {},
- "outputs": [],
- "source": [
- "from idaes.core.util.tables import (\n",
- " create_stream_table_dataframe,\n",
- " stream_table_dataframe_to_string,\n",
- ")\n",
- "\n",
- "st = create_stream_table_dataframe({\"Reactor\": m.fs.s05, \"Light Gases\": m.fs.s06})\n",
- "print(stream_table_dataframe_to_string(st))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "You can querry additional variables here if you like. \n",
- "\n",
- "Use Shift+Enter to run the cell once you have typed in your code. \n",
- "
"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Optimization\n",
- "\n",
- "\n",
- "We saw from the results above that the total operating cost for the base case was $442,297 per year. We are producing 0.162 mol/s of benzene at a purity of 89.5%. However, we are losing around 43.3% of benzene in F101 vapor outlet stream. \n",
- "\n",
- "Let us try to minimize this cost such that:\n",
- "- we are producing at least 0.18 mol/s of benzene as distillate i.e. our product stream\n",
- "- purity of benzene i.e. the mole fraction of benzene in the distillate is at least 99%\n",
- "- restricting the benzene loss in F101 vapor outlet to less than 20%\n",
- "\n",
- "For this problem, our decision variables are as follows:\n",
- "- H101 outlet temperature\n",
- "- R101 outlet temperature\n",
- "- F101 outlet temperature\n",
- "- H102 outlet temperature\n",
- "- Condenser pressure\n",
- "- reflux ratio\n",
- "- boilup ratio\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Let us declare our objective function for this problem. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 60,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.objective = Objective(expr=m.fs.operating_cost + m.fs.capital_cost)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Now, we need to unfix the decision variables as we had solved a square problem (degrees of freedom = 0) until now. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 61,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.H101.outlet.temperature.unfix()\n",
- "m.fs.R101.conversion.unfix()\n",
- "m.fs.F101.vap_outlet.temperature.unfix()\n",
- "m.fs.D101.condenser.condenser_pressure.unfix()\n",
- "m.fs.D101.condenser.reflux_ratio.unfix()\n",
- "m.fs.D101.reboiler.boilup_ratio.unfix()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "Let us now unfix the remaining variable: the temperature of the outlet from H102\n",
- "\n",
- "Use Shift+Enter to run the cell once you have typed in your code. \n",
- "
\n",
- "\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 62,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Unfix the temperature of the outlet from H102"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 63,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Unfix the temperature of the outlet from H102\n",
- "m.fs.H102.outlet.temperature.unfix()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Next, we need to set bounds on these decision variables to values shown below:\n",
- "\n",
- " - H101 outlet temperature [500, 600] K\n",
- " - R101 outlet temperature [600, 900] K\n",
- " - F101 outlet temperature [298, 450] K\n",
- " - H102 outlet temperature [350, 400] K\n",
- " - D101 condenser pressure [101325, 150000] Pa\n",
- " - D101 reflux ratio [0.1, 5]\n",
- " - D101 boilup ratio [0.1, 5]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 64,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Set bounds on the temperature of the outlet from H101\n",
- "m.fs.H101.outlet.temperature[0].setlb(500)\n",
- "m.fs.H101.outlet.temperature[0].setub(600)\n",
- "\n",
- "# Set bounds on the temperature of the outlet from R101\n",
- "m.fs.R101.outlet.temperature[0].setlb(600)\n",
- "m.fs.R101.outlet.temperature[0].setub(900)\n",
- "\n",
- "# Set bounds on the volume of the reactor R101\n",
- "m.fs.R101.volume[0].setlb(0)\n",
- "\n",
- "# Set bounds on the temperature of the vapor outlet from F101\n",
- "m.fs.F101.vap_outlet.temperature[0].setlb(298)\n",
- "m.fs.F101.vap_outlet.temperature[0].setub(450.0)\n",
- "\n",
- "# Set bounds on the temperature of the outlet from H102\n",
- "m.fs.H102.outlet.temperature[0].setlb(350)\n",
- "m.fs.H102.outlet.temperature[0].setub(400)\n",
- "\n",
- "# Set bounds on the pressure inside the condenser\n",
- "m.fs.D101.condenser.condenser_pressure.setlb(101325)\n",
- "m.fs.D101.condenser.condenser_pressure.setub(150000)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "Now, set the bounds for the D101 reflux ratio and boilup ratio.\n",
- "\n",
- "Use Shift+Enter to run the cell once you have typed in your code. \n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 65,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Set bounds on the reflux ratio\n",
- "\n",
- "\n",
- "# Todo: Set bounds on the boilup ratio"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 66,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Set bounds on the reflux ratio\n",
- "m.fs.D101.condenser.reflux_ratio.setlb(0.1)\n",
- "m.fs.D101.condenser.reflux_ratio.setub(5)\n",
- "\n",
- "# Todo: Set bounds on the boilup ratio\n",
- "m.fs.D101.reboiler.boilup_ratio.setlb(0.1)\n",
- "m.fs.D101.reboiler.boilup_ratio.setub(5)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Now, the only things left to define are our constraints on overhead loss in F101, distillate flowrate and its purity. Let us first look at defining a constraint for the overhead loss in F101 where we are restricting the benzene leaving the vapor stream to less than 20 % of the benzene available in the reactor outlet. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 67,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Ensure that the overhead loss of benzene from F101 <= 20%\n",
- "m.fs.overhead_loss = Constraint(\n",
- " expr=m.fs.F101.vap_outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]\n",
- " <= 0.20 * m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "Now, add the constraint such that we are producing at least 0.18 mol/s of benzene in the product stream which is the distillate of D101. Let us name this constraint as m.fs.product_flow. \n",
- "\n",
- "Use Shift+Enter to run the cell once you have typed in your code. \n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 68,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Add minimum product flow constraint"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 69,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Add minimum product flow constraint\n",
- "m.fs.product_flow = Constraint(expr=m.fs.D101.condenser.distillate.flow_mol[0] >= 0.18)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Let us add the final constraint on product purity or the mole fraction of benzene in the distillate such that it is at least greater than 99%. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 70,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.product_purity = Constraint(\n",
- " expr=m.fs.D101.condenser.distillate.mole_frac_comp[0, \"benzene\"] >= 0.99\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "We have now defined the optimization problem and we are now ready to solve this problem. \n",
- "\n",
- "\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 71,
- "metadata": {},
- "outputs": [],
- "source": [
- "results = solver.solve(m, tee=True)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Optimization Results\n",
- "\n",
- "Display the results and product specifications"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 73,
- "metadata": {},
- "outputs": [],
- "source": [
- "print(\"total cost = $\", value(m.fs.capital_cost) + value(m.fs.operating_cost))\n",
- "print(\"operating cost = $\", value(m.fs.operating_cost))\n",
- "print(\"capital cost = $\", value(m.fs.capital_cost))\n",
- "print()\n",
- "print(\n",
- " \"Distillate flowrate = \",\n",
- " value(m.fs.D101.condenser.distillate.flow_mol[0]()),\n",
- " \"mol/s\",\n",
- ")\n",
- "print(\n",
- " \"Benzene purity = \",\n",
- " 100 * value(m.fs.D101.condenser.distillate.mole_frac_comp[0, \"benzene\"]),\n",
- " \"%\",\n",
- ")\n",
- "print(\"Residue flowrate = \", value(m.fs.D101.reboiler.bottoms.flow_mol[0]()), \"mol/s\")\n",
- "print(\n",
- " \"Toluene purity = \",\n",
- " 100 * value(m.fs.D101.reboiler.bottoms.mole_frac_comp[0, \"toluene\"]),\n",
- " \"%\",\n",
- ")\n",
- "print()\n",
- "print(\"Conversion = \", 100 * value(m.fs.R101.conversion), \"%\")\n",
- "print()\n",
- "print(\n",
- " \"Overhead benzene loss in F101 = \",\n",
- " 100\n",
- " * value(m.fs.F101.vap_outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"])\n",
- " / value(m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]),\n",
- " \"%\",\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Display optimal values for the decision variables"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 75,
- "metadata": {},
- "outputs": [],
- "source": [
- "print(\"Optimal Values\")\n",
- "print()\n",
- "\n",
- "print(\"H101 outlet temperature = \", value(m.fs.H101.outlet.temperature[0]), \"K\")\n",
- "\n",
- "print()\n",
- "print(\"R101 outlet temperature = \", value(m.fs.R101.outlet.temperature[0]), \"K\")\n",
- "\n",
- "print()\n",
- "print(\"F101 outlet temperature = \", value(m.fs.F101.vap_outlet.temperature[0]), \"K\")\n",
- "\n",
- "print()\n",
- "print(\"H102 outlet temperature = \", value(m.fs.H102.outlet.temperature[0]), \"K\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Key Takeaways\n",
- "\n",
- "Observe that the optimization was able to reduce the yearly operating cost from \\\\$427,593 to \\\\$408,342 (~4.5%). However, the amortized capital cost more than doubled from \\\\$14,704 to \\\\$29,927 due to the need to increase the conversion in the reactor (from 75% to 93%) to meet the production and purity constraints. \n",
- "\n",
- "Further, observe that the product flow rate and product purity are at their minimum values (0.18 mol/s and 99%, respectively). This is expected as increasing recovery would require more energy and cost to purify the product.\n",
- "\n",
- "\n",
- "Finally, observe that the operating temperature of the flash (F101) is almost at its lower bound. This helps in minimizing the amount of benzene in the vapor stream leaving the flash."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
+ "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": [
+ "\n",
+ "# HDA Flowsheet Simulation and Optimization\n",
+ "Maintainer: Brandon Paul \n",
+ "Author: Brandon Paul \n",
+ "Updated: 2023-06-01 \n",
+ "\n",
+ "\n",
+ "## Note\n",
+ "\n",
+ "This tutorial will be similar to the HDA flowsheet tutorial in the Tutorials section, except that we use a distillation column instead of a second flash (F102) to produce benzene and toluene products.\n",
+ "\n",
+ "\n",
+ "## Learning outcomes\n",
+ "\n",
+ "\n",
+ "- Construct a steady-state flowsheet using the IDAES unit model library\n",
+ "- Connecting unit models in a flowsheet using Arcs\n",
+ "- Using the SequentialDecomposition tool to initialize a flowsheet with recycle\n",
+ "- Fomulate and solve an optimization problem\n",
+ " - Defining an objective function\n",
+ " - Setting variable bounds\n",
+ " - Adding additional constraints \n",
+ "\n",
+ "\n",
+ "## Problem Statement\n",
+ "\n",
+ "Hydrodealkylation is a chemical reaction that often involves reacting\n",
+ "an aromatic hydrocarbon in the presence of hydrogen gas to form a\n",
+ "simpler aromatic hydrocarbon devoid of functional groups. In this\n",
+ "example, toluene will be reacted with hydrogen gas at high temperatures\n",
+ " to form benzene via the following reaction:\n",
+ "\n",
+ "**C6H5CH3 + H2 → C6H6 + CH4**\n",
+ "\n",
+ "\n",
+ "This reaction is often accompanied by an equilibrium side reaction\n",
+ "which forms diphenyl, which we will neglect for this example.\n",
+ "\n",
+ "This example is based on the 1967 AIChE Student Contest problem as\n",
+ "present by Douglas, J.M., Chemical Design of Chemical Processes, 1988,\n",
+ "McGraw-Hill.\n",
+ "\n",
+ "The flowsheet that we will be using for this module is shown below with the stream conditions. We will be processing toluene and hydrogen to produce at least 370 TPY of benzene. As shown in the flowsheet, we use a flash tank, F101, to separate out the non-condensibles, and a distillation column, D101, to further separate the benzene-toluene mixture to improve the benzene purity. The non-condensibles separated out in F101 will be partially recycled back to M101 and the rest will be purged. We will assume ideal gas behavior for this flowsheet. The properties required for this module are defined in\n",
+ "\n",
+ "- `hda_ideal_VLE.py`\n",
+ "- `idaes.models.properties.activity_coeff_models.BTX_activity_coeff_VLE`\n",
+ "- `hda_reaction.py`\n",
+ "\n",
+ "We will be using two thermodynamic packages: one (first in the list above) containing all four components (i.e., toluene, hydrogen, benzene, and methane) and the other (second in the list above) containing benzene and toluene only. The latter is needed to simplify the VLE calculations in the distillation column model. \n",
+ "\n",
+ "![](HDA_flowsheet_distillation.png)\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Translator block\n",
+ "\n",
+ "Benzene and toluene are separated by distillation, so the process involves phase equilibrium and two-phase flow conditions. However, the presence of hydrogen and methane complicates the calculations. This is because, hydrogen and methane are non-condensable under all conditions of interest; ergo, a vapor phase will always be present, and the mixture bubble point is extremely low. To simplify the phase equilibrium calculations, hydrogen and methane will be considered completely as non-condensable and insoluble in the liquid outlet from the flash F101.\n",
+ "\n",
+ "Since no hydrogen and methane will be present in the unit operations following the flash, a different component list can be used to simplify the property calculations. IDAES supports the definition of multiple property packages within a single flowsheet via `Translator` blocks. `Translator` blocks convert between different property calculations, component lists, and equations of state. "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Importing required pyomo and idaes components\n",
+ "\n",
+ "\n",
+ "To construct a flowsheet, we will need several components from the pyomo and idaes package. Let us first import the following components from Pyomo:\n",
+ "- Constraint (to write constraints)\n",
+ "- Var (to declare variables)\n",
+ "- ConcreteModel (to create the concrete model object)\n",
+ "- Expression (to evaluate values as a function of variables defined in the model)\n",
+ "- Objective (to define an objective function for optimization)\n",
+ "- SolverFactory (to solve the problem)\n",
+ "- TransformationFactory (to apply certain transformations)\n",
+ "- Arc (to connect two unit models)\n",
+ "- SequentialDecomposition (to initialize the flowsheet in a sequential mode)\n",
+ "\n",
+ "For further details on these components, please refer to the pyomo documentation: https://pyomo.readthedocs.io/en/stable/\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from pyomo.environ import (\n",
+ " Constraint,\n",
+ " Var,\n",
+ " ConcreteModel,\n",
+ " Expression,\n",
+ " Objective,\n",
+ " TransformationFactory,\n",
+ " value,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "Import `Arc` and `SequentialDecomposition` tools from `pyomo.network`\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Import the above mentioned tools from pyomo.network"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Import the above mentioned tools from pyomo.network\n",
+ "from pyomo.network import Arc, SequentialDecomposition"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "From IDAES, we will be needing the FlowsheetBlock and the following unit models:\n",
+ "- Mixer\n",
+ "- Heater\n",
+ "- CSTR\n",
+ "- Flash\n",
+ "- Separator (splitter) \n",
+ "- PressureChanger\n",
+ "- Translator (to switch from one property package to another)\n",
+ "- TrayColumn (distillation column)\n",
+ "- CondenserType (Type of the overhead condenser: complete or partial)\n",
+ "- TemperatureSpec (Temperature specification inside the condenser)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from idaes.core import FlowsheetBlock"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from idaes.models.unit_models import (\n",
+ " PressureChanger,\n",
+ " Mixer,\n",
+ " Separator as Splitter,\n",
+ " Heater,\n",
+ " CSTR,\n",
+ " Flash,\n",
+ " Translator,\n",
+ ")\n",
+ "\n",
+ "from idaes.models_extra.column_models import TrayColumn\n",
+ "from idaes.models_extra.column_models.condenser import CondenserType, TemperatureSpec"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We will also be needing some utility tools to put together the flowsheet and calculate the degrees of freedom. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Utility tools to put together the flowsheet and calculate the degrees of freedom\n",
+ "from idaes.models.unit_models.pressure_changer import ThermodynamicAssumption\n",
+ "from idaes.core.util.model_statistics import degrees_of_freedom\n",
+ "from idaes.core.util.initialization import propagate_state\n",
+ "from idaes.core.solvers import get_solver\n",
+ "import idaes.core.util.scaling as iscale\n",
+ "from idaes.core.util.exceptions import InitializationError\n",
+ "\n",
+ "# Import idaes logger to set output levels\n",
+ "import idaes.logger as idaeslog"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Importing required thermo and reaction packages\n",
+ "\n",
+ "Finally, we import the thermophysical (`ideal_VLE.py` and `BTXParameterBlock`) packages and reaction package (`reaction.py`) for the HDA process. We have created custom thermophysical packages that assume ideal gas behavior with support for VLE. The reaction package consists of the stochiometric coefficients for the reaction, heat of reaction, and kinetic information (Arrhenius constant and activation energy). "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from idaes_examples.mod.hda import hda_reaction as reaction_props\n",
+ "from idaes.models.properties.activity_coeff_models.BTX_activity_coeff_VLE import (\n",
+ " BTXParameterBlock,\n",
+ ")\n",
+ "\n",
+ "from idaes_examples.mod.hda.hda_ideal_VLE import HDAParameterBlock"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Constructing the Flowsheet\n",
+ "\n",
+ "We have now imported all the components, unit models, and property modules we need to construct a flowsheet. Let us create a ConcreteModel and add the flowsheet block to it. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Create a Pyomo Concrete Model to contain the problem\n",
+ "m = ConcreteModel()\n",
+ "\n",
+ "# Add a steady state flowsheet block to the model\n",
+ "m.fs = FlowsheetBlock(dynamic=False)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We will now add the thermophysical and reaction packages to the flowsheet."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Property package for benzene, toluene, hydrogen, methane mixture\n",
+ "m.fs.BTHM_params = HDAParameterBlock()\n",
+ "\n",
+ "# Property package for the benzene-toluene mixture\n",
+ "m.fs.BT_params = BTXParameterBlock(\n",
+ " valid_phase=(\"Liq\", \"Vap\"), activity_coeff_model=\"Ideal\"\n",
+ ")\n",
+ "\n",
+ "# Reaction package for the HDA reaction\n",
+ "m.fs.reaction_params = reaction_props.HDAReactionParameterBlock(\n",
+ " property_package=m.fs.BTHM_params\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Adding Unit Models\n",
+ "\n",
+ "Let us start adding the unit models we have imported to the flowsheet. Here, we are adding the Mixer (assigned a name M101) and a Heater (assigned a name H101). Note that, all unit models need to be given a property package argument. In addition, the Mixer unit model needs a `list` consisting of the inlets (toluene feed, hydrogen feed and vapor recycle streams in this case). "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Adding the mixer M101 to the flowsheet\n",
+ "m.fs.M101 = Mixer(\n",
+ " property_package=m.fs.BTHM_params,\n",
+ " inlet_list=[\"toluene_feed\", \"hydrogen_feed\", \"vapor_recycle\"],\n",
+ ")\n",
+ "\n",
+ "# Adding the heater H101 to the flowsheet\n",
+ "m.fs.H101 = Heater(property_package=m.fs.BTHM_params, has_phase_equilibrium=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "
Inline Exercise:\n",
+ "Let us now add the CSTR (assign the name R101) and pass the following arguments:\n",
+ "
\n",
+ " - \"property_package\": m.fs.BTHM_params
\n",
+ " - \"reaction_package\": m.fs.reaction_params
\n",
+ " - \"has_heat_of_reaction\": True
\n",
+ " - \"has_heat_transfer\": True
\n",
+ "
\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Add reactor with the specifications above"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Add reactor with the specifications above\n",
+ "m.fs.R101 = CSTR(\n",
+ " property_package=m.fs.BTHM_params,\n",
+ " reaction_package=m.fs.reaction_params,\n",
+ " has_heat_of_reaction=True,\n",
+ " has_heat_transfer=True,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let us now add the Flash (assign the name F101), Splitter (assign the name S101) and PressureChanger (assign the name C101)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Adding the flash tank F101 to the flowsheet\n",
+ "m.fs.F101 = Flash(\n",
+ " property_package=m.fs.BTHM_params, has_heat_transfer=True, has_pressure_change=True\n",
+ ")\n",
+ "\n",
+ "# Adding the splitter S101 to the flowsheet\n",
+ "m.fs.S101 = Splitter(\n",
+ " property_package=m.fs.BTHM_params, outlet_list=[\"purge\", \"recycle\"]\n",
+ ")\n",
+ "\n",
+ "# Adding the compressor C101 to the flowsheet\n",
+ "m.fs.C101 = PressureChanger(\n",
+ " property_package=m.fs.BTHM_params,\n",
+ " compressor=True,\n",
+ " thermodynamic_assumption=ThermodynamicAssumption.isothermal,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Remark\n",
+ "\n",
+ "Currently, the `SequentialDecomposition()` tool, which we will later be using to initialize the flowsheet, does not support the distillation column model. Thus, we will first simulate the flowsheet without the distillation column. After it converges, we will then add the distillation column, initialize it, and simulate the entire flowsheet."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As mentioned above, we use the `m.fs.BTHM_params` package, which contains all the four species, for the reactor loop, and the simpler `m.fs.BT_params` for unit operations following the flash (i.e., heater H102 and the distillation column D101). We define a `Translator` block to link the source property package and the package it is to be translated to in the following manner:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Add translator block to convert between property packages\n",
+ "m.fs.translator = Translator(\n",
+ " inlet_property_package=m.fs.BTHM_params, outlet_property_package=m.fs.BT_params\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Translator block constraints\n",
+ "\n",
+ "The `Translator` block needs to know how to translate between the two property packages. This must be custom coded for each application because of the generality of the IDAES framework.\n",
+ "\n",
+ "For this process, five constraints are required based on the state variables used in the outgoing process.\n",
+ "\n",
+ "- Since we assumed that only benzene and toluene are present in the liquid phase, the total molar flowrate must be the sum of molar flowrates of benzene and toluene, respectively.\n",
+ "- Temperature of the inlet and outlet streams must be the same.\n",
+ "- Pressure of the inlet and outgoing streams must be the same\n",
+ "- The mole fraction of benzene in the outgoing stream is the ratio of the molar flowrate of liquid benzene in the inlet to the sum of molar flowrates of liquid benzene and toluene in the inlet.\n",
+ "- The mole fraction of toluene in the outgoing stream is the ratio of the molar flowrate of liquid toluene in the inlet to the sum of molar flowrates of liquid benzene and toluene in the inlet."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Add constraint: Total flow = benzene flow + toluene flow (molar)\n",
+ "m.fs.translator.eq_total_flow = Constraint(\n",
+ " expr=m.fs.translator.outlet.flow_mol[0]\n",
+ " == m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
+ " + m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
+ ")\n",
+ "\n",
+ "# Add constraint: Outlet temperature = Inlet temperature\n",
+ "m.fs.translator.eq_temperature = Constraint(\n",
+ " expr=m.fs.translator.outlet.temperature[0] == m.fs.translator.inlet.temperature[0]\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In the above, note that the variable flow_mol_phase_comp has the index - [time, phase, component]. As this is a steady-state flowsheet, the time index by default is 0. The valid phases are [\"Liq\", \"Vap\"]. Similarly the valid component list is [\"benzene\", \"toluene\", \"hydrogen\", \"methane\"]."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "Add the constraint to ensure that the outlet pressure is the same as the inlet pressure\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Add constraint: Outlet pressure = Inlet pressure"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Add constraint: Outlet pressure = Inlet pressure\n",
+ "m.fs.translator.eq_pressure = Constraint(\n",
+ " expr=m.fs.translator.outlet.pressure[0] == m.fs.translator.inlet.pressure[0]\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Remaining constraints on the translator block\n",
+ "\n",
+ "# Add constraint: Benzene mole fraction definition\n",
+ "m.fs.translator.eq_mole_frac_benzene = Constraint(\n",
+ " expr=m.fs.translator.outlet.mole_frac_comp[0, \"benzene\"]\n",
+ " == m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
+ " / (\n",
+ " m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
+ " + m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
+ " )\n",
+ ")\n",
+ "\n",
+ "# Add constraint: Toluene mole fraction definition\n",
+ "m.fs.translator.eq_mole_frac_toluene = Constraint(\n",
+ " expr=m.fs.translator.outlet.mole_frac_comp[0, \"toluene\"]\n",
+ " == m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
+ " / (\n",
+ " m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
+ " + m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
+ " )\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "Finally, let us add the Heater H102 in the same way as H101 but pass the m.fs.BT_params thermodynamic package. We will add the distillation column after converging the flowsheet.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 20,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Add the Heater H102 to the flowsheet"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Add the Heater H102 to the flowsheet\n",
+ "m.fs.H102 = Heater(\n",
+ " property_package=m.fs.BT_params,\n",
+ " has_pressure_change=True,\n",
+ " has_phase_equilibrium=True,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Connecting Unit Models using Arcs\n",
+ "\n",
+ "We have now added the initial set of unit models to the flowsheet. However, we have not yet specified how the units are connected. To do this, we will be using the `Arc` which is a pyomo component that takes in two arguments: `source` and `destination`. Let us connect the outlet of the mixer (M101) to the inlet of the heater (H101). "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.fs.s03 = Arc(source=m.fs.M101.outlet, destination=m.fs.H101.inlet)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "![](HDA_flowsheet_distillation.png) \n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Now, connect the H101 outlet to the R101 inlet using the cell above as a guide. \n",
+ "
\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 23,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Connect the H101 outlet to R101 inlet"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Connect the H101 outlet to R101 inlet\n",
+ "m.fs.s04 = Arc(source=m.fs.H101.outlet, destination=m.fs.R101.inlet)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We will now be connecting the rest of the units as shown below. Notice how the outlet names are different for the flash tank as it has a vapor and a liquid outlet. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.fs.s05 = Arc(source=m.fs.R101.outlet, destination=m.fs.F101.inlet)\n",
+ "m.fs.s06 = Arc(source=m.fs.F101.vap_outlet, destination=m.fs.S101.inlet)\n",
+ "m.fs.s08 = Arc(source=m.fs.S101.recycle, destination=m.fs.C101.inlet)\n",
+ "m.fs.s09 = Arc(source=m.fs.C101.outlet, destination=m.fs.M101.vapor_recycle)\n",
+ "m.fs.s10a = Arc(source=m.fs.F101.liq_outlet, destination=m.fs.translator.inlet)\n",
+ "m.fs.s10b = Arc(source=m.fs.translator.outlet, destination=m.fs.H102.inlet)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We have now connected the unit model block using the arcs. However, each of these arcs link to ports on the two unit models that are connected. In this case, the ports consist of the state variables that need to be linked between the unit models. Pyomo provides a convenient method to write these equality constraints for us between two ports and this is done as follows:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 26,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "TransformationFactory(\"network.expand_arcs\").apply_to(m)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Appending additional constraints to the model\n",
+ "\n",
+ "Now, we will see how we can add additional constraints to the model using `Constraint` from Pyomo.\n",
+ "\n",
+ "Consider the reactor R101. By default, the conversion of a component is not calculated when we simulate the flowsheet. If we are interested either in specifying or constraining the conversion value, we can add the following constraint to calculate the conversion:\n",
+ "$$ \\text{Conversion of toluene} = \\frac{\\text{molar flow of toluene in the inlet} - \\text{molar flow of toluene in the outlet}}{\\text{molar flow of toluene in the inlet}} $$ \n",
+ "\n",
+ "We add the constraint to the model as shown below."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 27,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Define the conversion variables using 'Var'\n",
+ "m.fs.R101.conversion = Var(initialize=0.75, bounds=(0, 1))\n",
+ "\n",
+ "# Append the constraint to the model\n",
+ "m.fs.R101.conv_constraint = Constraint(\n",
+ " expr=m.fs.R101.conversion * m.fs.R101.inlet.flow_mol_phase_comp[0, \"Vap\", \"toluene\"]\n",
+ " == (\n",
+ " m.fs.R101.inlet.flow_mol_phase_comp[0, \"Vap\", \"toluene\"]\n",
+ " - m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"toluene\"]\n",
+ " )\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Fixing feed conditions and Initializing the flowsheet\n",
+ "\n",
+ "Let us first check how many degrees of freedom exist for this flowsheet using the `degrees_of_freedom` tool we imported earlier. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 28,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "29\n"
+ ]
}
- ],
- "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.8.16"
+ ],
+ "source": [
+ "print(degrees_of_freedom(m))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We will now be fixing the toluene feed stream to the conditions shown in the flowsheet above. Please note that though this is a pure toluene feed, the remaining components are still assigned a very small non-zero value to help with convergence and initializing. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 30,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"benzene\"].fix(1e-5)\n",
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"toluene\"].fix(1e-5)\n",
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"hydrogen\"].fix(1e-5)\n",
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"methane\"].fix(1e-5)\n",
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"benzene\"].fix(1e-5)\n",
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"toluene\"].fix(0.30)\n",
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"hydrogen\"].fix(1e-5)\n",
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"methane\"].fix(1e-5)\n",
+ "m.fs.M101.toluene_feed.temperature.fix(303.2)\n",
+ "m.fs.M101.toluene_feed.pressure.fix(350000)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Similarly, let us fix the hydrogen feed to the following conditions in the next cell:\n",
+ " \n",
+ " - FH2 = 0.30 mol/s
\n",
+ " - FCH4 = 0.02 mol/s
\n",
+ " - Remaining components = 1e-5 mol/s
\n",
+ " - T = 303.2 K
\n",
+ " - P = 350000 Pa
\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 31,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"benzene\"].fix(1e-5)\n",
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"toluene\"].fix(1e-5)\n",
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"hydrogen\"].fix(0.30)\n",
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"methane\"].fix(0.02)\n",
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"benzene\"].fix(1e-5)\n",
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"toluene\"].fix(1e-5)\n",
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"hydrogen\"].fix(1e-5)\n",
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"methane\"].fix(1e-5)\n",
+ "m.fs.M101.hydrogen_feed.temperature.fix(303.2)\n",
+ "m.fs.M101.hydrogen_feed.pressure.fix(350000)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Fixing unit model specifications\n",
+ "\n",
+ "Now that we have fixed our inlet feed conditions, we will now be fixing the operating conditions for the unit models in the flowsheet. Let us set the H101 outlet temperature to 600 K. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 32,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Fix the temperature of the outlet from the heater H101\n",
+ "m.fs.H101.outlet.temperature.fix(600)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "
Inline Exercise:\n",
+ "Set the conditions for the reactor R101 to the following conditions:\n",
+ "
\n",
+ " - `conversion` = 0.75
\n",
+ " - `heat_duty` = 0
\n",
+ "
\n",
+ "\n",
+ "Use Shift+Enter to run the cell once you have typed in your code. \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 33,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Fix the 'conversion' of the reactor R101\n",
+ "\n",
+ "\n",
+ "# Todo: Fix the 'heat_duty' of the reactor R101"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 34,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Fix the 'conversion' of the reactor R101\n",
+ "m.fs.R101.conversion.fix(0.75)\n",
+ "\n",
+ "# Todo: Fix the 'heat_duty' of the reactor R101\n",
+ "m.fs.R101.heat_duty.fix(0)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The Flash conditions for F101 can be set as follows. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 35,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Fix the temperature of the vapor outlet from F101\n",
+ "m.fs.F101.vap_outlet.temperature.fix(325.0)\n",
+ "\n",
+ "# Fix the pressure drop in the flash F101\n",
+ "m.fs.F101.deltaP.fix(0)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let us fix the split fraction of the purge stream from the splitter S101 and the outlet pressure from the compressor C101"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 36,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Fix the split fraction of the 'purge' stream from S101\n",
+ "m.fs.S101.split_fraction[0, \"purge\"].fix(0.2)\n",
+ "\n",
+ "# Fix the pressure of the outlet from the compressor C101\n",
+ "m.fs.C101.outlet.pressure.fix(350000)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Finally, let us fix the temperature of the outlet from H102 and the pressure drop in H102 as the following"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 37,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Fix the temperature of the outlet from the heater H102\n",
+ "m.fs.H102.outlet.temperature.fix(375)\n",
+ "\n",
+ "# Fix the pressure drop in the heater H102\n",
+ "m.fs.H102.deltaP.fix(-200000)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "To avoid convergence issues associated with poorly scaled variables and/or constraints, we scale the variables and constraints corresponding to the heaters H101 and H102, flash F101 and the reactor R101. Scaling factors for the flow rates, temperature, pressure, etc. have been defined in the property package: `ideal_VLE.py` file. Here, we set scaling factors only for the heat duty of the heater, the reaction extent, heat duty and volume of the reactor."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 38,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enth_mol_phase_comp[Liq,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enth_mol_phase_comp[Liq,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enth_mol_phase_comp[Vap,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enth_mol_phase_comp[Vap,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].enth_mol_phase_comp[Liq,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].enth_mol_phase_comp[Liq,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].enth_mol_phase_comp[Vap,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].enth_mol_phase_comp[Vap,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].material_flow_terms[Liq,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].material_flow_terms[Vap,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].material_flow_terms[Liq,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].material_flow_terms[Vap,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enthalpy_flow_terms[Liq], enthalpy_flow_terms\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enthalpy_flow_terms[Vap], enthalpy_flow_terms\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Set scaling factors for heat duty, reaction extent and volume\n",
+ "iscale.set_scaling_factor(m.fs.H101.control_volume.heat, 1e-2)\n",
+ "iscale.set_scaling_factor(m.fs.R101.control_volume.heat, 1e-2)\n",
+ "iscale.set_scaling_factor(m.fs.R101.control_volume.rate_reaction_extent, 1)\n",
+ "iscale.set_scaling_factor(m.fs.R101.control_volume.volume, 1)\n",
+ "iscale.set_scaling_factor(m.fs.F101.control_volume.heat, 1e-2)\n",
+ "iscale.set_scaling_factor(m.fs.H102.control_volume.heat, 1e-2)\n",
+ "\n",
+ "# Set the scaling factors for the remaining variables and all constraints\n",
+ "iscale.calculate_scaling_factors(m.fs.H101)\n",
+ "iscale.calculate_scaling_factors(m.fs.R101)\n",
+ "iscale.calculate_scaling_factors(m.fs.F101)\n",
+ "iscale.calculate_scaling_factors(m.fs.H102)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "We have now defined all the feed conditions and the inputs required for the unit models. The system should now have 0 degrees of freedom i.e. should be a square problem. Please check that the degrees of freedom is 0. \n",
+ "\n",
+ "Use Shift+Enter to run the cell once you have typed in your code. \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 39,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Check the degrees of freedom"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 40,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "0\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Todo: Check the degrees of freedom\n",
+ "print(degrees_of_freedom(m))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Initialization\n",
+ "\n",
+ "This subsection will demonstrate how to use the built-in sequential decomposition tool to initialize our flowsheet.\n",
+ "\n",
+ "Let us first create an object for the `SequentialDecomposition` and specify our options for this. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 42,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "seq = SequentialDecomposition()\n",
+ "seq.options.select_tear_method = \"heuristic\"\n",
+ "seq.options.tear_method = \"Wegstein\"\n",
+ "seq.options.iterLim = 3\n",
+ "\n",
+ "# Using the SD tool\n",
+ "G = seq.create_graph(m)\n",
+ "heuristic_tear_set = seq.tear_set_arcs(G, method=\"heuristic\")\n",
+ "order = seq.calculation_order(G)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Which is the tear stream? Display tear set and order"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 43,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "fs.s03\n"
+ ]
}
+ ],
+ "source": [
+ "for o in heuristic_tear_set:\n",
+ " print(o.name)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "What sequence did the SD tool determine to solve this flowsheet with the least number of tears? "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 44,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "fs.H101\n",
+ "fs.R101\n",
+ "fs.F101\n",
+ "fs.S101\n",
+ "fs.C101\n",
+ "fs.M101\n"
+ ]
+ }
+ ],
+ "source": [
+ "for o in order:\n",
+ " print(o[0].name)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The SequentialDecomposition tool has determined that the tear stream is the mixer outlet (s03 in the Figure above). We will need to provide a reasonable guess for this.\n",
+ "\n",
+ "For the initial guess, we assume that the flowrate of the recycle stream (s09) is zero. Consequently, the flow rate of the stream s03 is simply the sum of the flowrates of the toluene feed and hydrogen feed streams. Further, since the temperature and the pressure of both the toluene and hydrogen feed streams are the same, we specify their values as the initial guess for the temperature and pressure of the stream s03."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 45,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "tear_guesses = {\n",
+ " \"flow_mol_phase_comp\": {\n",
+ " (0, \"Vap\", \"benzene\"): 1e-5,\n",
+ " (0, \"Vap\", \"toluene\"): 1e-5,\n",
+ " (0, \"Vap\", \"hydrogen\"): 0.30,\n",
+ " (0, \"Vap\", \"methane\"): 0.02,\n",
+ " (0, \"Liq\", \"benzene\"): 1e-5,\n",
+ " (0, \"Liq\", \"toluene\"): 0.30,\n",
+ " (0, \"Liq\", \"hydrogen\"): 1e-5,\n",
+ " (0, \"Liq\", \"methane\"): 1e-5,\n",
+ " },\n",
+ " \"temperature\": {0: 303.2},\n",
+ " \"pressure\": {0: 350000},\n",
+ "}\n",
+ "\n",
+ "# Pass the tear_guess to the SD tool\n",
+ "seq.set_guesses_for(m.fs.H101.inlet, tear_guesses)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Next, we need to tell the tool how to initialize a particular unit. We will be writing a python function which takes in a \"unit\" and calls the initialize method on that unit. For the initialization, we will import a Block Triangularization Initializer which decomposes the model into a set of subproblems. These subproblems are solved using a block triangularization transformation before applying a simple Newton or user-selected solver. Methods such as block triangularization often solve faster and yield more reliable behavior than heuristic methods, but sometime struggle to decompose models with strongly coupled equations (e.g. column models, systems with counter-current flow, vapor-liquid equilibrium)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 46,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def function(unit):\n",
+ " # Try initializing using default initializer,\n",
+ " # if it fails (probably due to scaling) try for the second time\n",
+ " try:\n",
+ " initializer = unit.default_initializer()\n",
+ " initializer.initialize(unit, output_level=idaeslog.INFO)\n",
+ " except InitializationError:\n",
+ " solver = get_solver()\n",
+ " solver.solve(unit)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We are now ready to initialize our flowsheet in a sequential mode. Note that we specifically set the iteration limit to be 3 as we are trying to use this tool only to get a good set of initial values such that IPOPT can then take over and solve this flowsheet for us. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 47,
+ "metadata": {
+ "scrolled": false
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2024-08-28 18:38:14 [INFO] idaes.init.fs.H101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:16 [INFO] idaes.init.fs.H101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:16 [INFO] idaes.init.fs.R101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:16 [INFO] idaes.init.fs.R101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.F101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.F101.control_volume.properties_out: Initialization Complete\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 12\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 50 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.S101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - \n",
+ "2024-08-28 18:38:18 [INFO] idaes.init.fs.C101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:18 [INFO] idaes.init.fs.C101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:18 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 1 optimal - .\n",
+ "2024-08-28 18:38:18 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 2 optimal - .\n",
+ "2024-08-28 18:38:19 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 3 optimal - .\n",
+ "2024-08-28 18:38:19 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 4 optimal - .\n",
+ "2024-08-28 18:38:19 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 5 optimal - .\n",
+ "2024-08-28 18:38:19 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 1 optimal - .\n",
+ "2024-08-28 18:38:19 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 2 optimal - .\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 3 optimal - .\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 4 optimal - .\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 5 optimal - .\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.M101.toluene_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.M101.hydrogen_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.M101.vapor_recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:21 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:21 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - \n",
+ "2024-08-28 18:38:21 [INFO] idaes.init.fs.H101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:21 [INFO] idaes.init.fs.H101.control_volume.properties_out: Initialization Complete\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 11\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 50 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.R101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.R101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.F101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.F101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.S101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - \n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.C101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.C101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.M101.toluene_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.M101.hydrogen_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.M101.vapor_recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - \n",
+ "2024-08-28 18:38:24 [INFO] idaes.init.fs.H101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:24 [INFO] idaes.init.fs.H101.control_volume.properties_out: Initialization Complete\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 11\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 50 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "2024-08-28 18:38:24 [INFO] idaes.init.fs.R101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:24 [INFO] idaes.init.fs.R101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.F101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.F101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.S101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - \n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.C101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.C101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.M101.toluene_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.M101.hydrogen_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.M101.vapor_recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - \n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.H101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.H101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.R101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.R101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.F101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.F101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.S101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - \n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.C101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.C101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.M101.toluene_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.M101.hydrogen_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.M101.vapor_recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - \n",
+ "2024-08-28 18:38:29 [INFO] idaes.init.fs.H101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:29 [INFO] idaes.init.fs.H101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:29 [INFO] idaes.init.fs.R101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:29 [INFO] idaes.init.fs.R101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.F101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.F101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.S101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - \n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.C101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.C101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.M101.toluene_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.M101.hydrogen_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:31 [INFO] idaes.init.fs.M101.vapor_recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:31 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:31 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - \n",
+ "WARNING: Wegstein failed to converge in 3 iterations\n",
+ "2024-08-28 18:38:31 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 1 optimal - .\n",
+ "2024-08-28 18:38:31 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 2 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 3 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 4 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 5 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 1 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 2 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 3 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 4 optimal - .\n",
+ "2024-08-28 18:38:33 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 5 optimal - .\n"
+ ]
+ }
+ ],
+ "source": [
+ "seq.run(m, function)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "We have now initialized the flowsheet. Let us run the flowsheet in a simulation mode to look at the results. \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 48,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "WARNING: model contains export suffix 'scaling_factor' that contains 6\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 150 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "Ipopt 3.13.2: nlp_scaling_method=gradient-based\n",
+ "tol=1e-06\n",
+ "max_iter=200\n",
+ "\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...: 1097\n",
+ "Number of nonzeros in inequality constraint Jacobian.: 0\n",
+ "Number of nonzeros in Lagrangian Hessian.............: 877\n",
+ "\n",
+ "Total number of variables............................: 363\n",
+ " variables with only lower bounds: 8\n",
+ " variables with lower and upper bounds: 155\n",
+ " variables with only upper bounds: 0\n",
+ "Total number of equality constraints.................: 363\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.82e+04 1.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
+ " 1 0.0000000e+00 8.69e+03 1.44e+03 -1.0 2.00e+04 - 9.71e-01 4.67e-01H 1\n",
+ " 2 0.0000000e+00 1.29e+03 1.56e+03 -1.0 1.60e+04 - 9.79e-01 4.90e-01h 1\n",
+ " 3 0.0000000e+00 1.18e+03 1.55e+05 -1.0 1.40e+04 - 9.90e-01 4.99e-01h 1\n",
+ " 4 0.0000000e+00 5.46e+02 2.32e+09 -1.0 8.42e+03 - 1.00e+00 9.82e-01h 1\n",
+ " 5 0.0000000e+00 5.46e+03 3.66e+10 -1.0 5.97e+02 - 1.00e+00 9.90e-01h 1\n",
+ " 6 0.0000000e+00 1.21e+03 8.01e+09 -1.0 5.75e+00 - 1.00e+00 1.00e+00h 1\n",
+ " 7 0.0000000e+00 6.41e+00 3.87e+07 -1.0 1.53e-03 - 1.00e+00 1.00e+00f 1\n",
+ " 8 0.0000000e+00 1.96e-04 9.36e+02 -1.0 7.28e-06 - 1.00e+00 1.00e+00h 1\n",
+ " 9 0.0000000e+00 2.24e-08 4.99e-01 -3.8 5.92e-08 - 1.00e+00 1.00e+00h 1\n",
+ "Cannot recompute multipliers for feasibility problem. Error in eq_mult_calculator\n",
+ "\n",
+ "Number of Iterations....: 9\n",
+ "\n",
+ " (scaled) (unscaled)\n",
+ "Objective...............: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Dual infeasibility......: 1.5042487592972509e+04 1.5042487592972509e+04\n",
+ "Constraint violation....: 2.9103830456733704e-11 2.2351741790771484e-08\n",
+ "Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Overall NLP error.......: 2.9103830456733704e-11 1.5042487592972509e+04\n",
+ "\n",
+ "\n",
+ "Number of objective function evaluations = 11\n",
+ "Number of objective gradient evaluations = 10\n",
+ "Number of equality constraint evaluations = 11\n",
+ "Number of inequality constraint evaluations = 0\n",
+ "Number of equality constraint Jacobian evaluations = 10\n",
+ "Number of inequality constraint Jacobian evaluations = 0\n",
+ "Number of Lagrangian Hessian evaluations = 9\n",
+ "Total CPU secs in IPOPT (w/o function evaluations) = 0.011\n",
+ "Total CPU secs in NLP function evaluations = 0.001\n",
+ "\n",
+ "EXIT: Optimal Solution Found.\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Create the solver object\n",
+ "solver = get_solver()\n",
+ "\n",
+ "# Solve the model\n",
+ "results = solver.solve(m, tee=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Add distillation column \n",
+ "\n",
+ "As mentioned earlier, the `SequentialDecomposition` tool currently does not support the distillation column model. Thus, we have not included the distillation column in the flowsheet. Now that we have a converged flowsheet, we will add the distillation column and simulate the entire flowsheet. \n",
+ "\n",
+ "In the following, we will\n",
+ "- Add the distillation column \n",
+ "- Connect it to the heater \n",
+ "- Add the necessary equality constraints\n",
+ "- Propagate the state variable information from the outlet of the heater to the inlet of the distillation column \n",
+ "- Fix the degrees of freedom of the distillation block (reflux ratio, boilup ratio, and condenser pressure)\n",
+ "- Scale the control volume heat variables to help convergence\n",
+ "- Initialize the distillation block.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 50,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].material_flow_terms[Liq,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].material_flow_terms[Vap,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].material_flow_terms[Liq,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].material_flow_terms[Vap,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].enthalpy_flow_terms[Liq], enthalpy_flow_terms\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].enthalpy_flow_terms[Vap], enthalpy_flow_terms\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].material_flow_terms[Liq,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].material_flow_terms[Vap,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].material_flow_terms[Liq,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].material_flow_terms[Vap,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].enthalpy_flow_terms[Liq], enthalpy_flow_terms\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].enthalpy_flow_terms[Vap], enthalpy_flow_terms\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.e_flow_reflux[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.e_mole_frac_reflux[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.e_mole_frac_reflux[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.e_flow_vapor_reboil[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.e_mole_frac_vapor_reboil[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.e_mole_frac_vapor_reboil[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101: Begin initialization.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray: Begin initialization.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: State Released.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: State Released.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser.control_volume: Initialization Complete\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser: Initialization Complete, optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: State Released.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: State Released.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler: Initialization Complete, optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: State Released.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.rectification_section[1]: Begin initialization.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: State Released.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[2]: Begin initialization.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: State Released.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[2]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[2]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3]: Begin initialization.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: State Released.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[3]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[3]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[3]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4]: Begin initialization.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: State Released.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.stripping_section[6]: Begin initialization.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: State Released.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[7]: Begin initialization.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: State Released.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[7]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[7]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8]: Begin initialization.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: State Released.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[8]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[8]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[8]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9]: Begin initialization.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: State Released.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10]: Begin initialization.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: State Released.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: State Released.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101: Rectification section initialization status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101: Stripping section initialization status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: State Released.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: State Released.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101: Column section initialization status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: State Released.\n",
+ "2024-08-28 18:39:02 [INFO] idaes.init.fs.D101: Column section + Condenser initialization status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:02 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: State Released.\n",
+ "2024-08-28 18:39:03 [INFO] idaes.init.fs.D101: Column section + Condenser + Reboiler initialization status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:03 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: State Released.\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Add distillation column to the flowsheet\n",
+ "m.fs.D101 = TrayColumn(\n",
+ " number_of_trays=10,\n",
+ " feed_tray_location=5,\n",
+ " condenser_type=CondenserType.totalCondenser,\n",
+ " condenser_temperature_spec=TemperatureSpec.atBubblePoint,\n",
+ " property_package=m.fs.BT_params,\n",
+ ")\n",
+ "\n",
+ "# Connect the outlet from the heater H102 to the distillation column\n",
+ "m.fs.s11 = Arc(source=m.fs.H102.outlet, destination=m.fs.D101.feed)\n",
+ "\n",
+ "# Add the necessary equality constraints\n",
+ "TransformationFactory(\"network.expand_arcs\").apply_to(m)\n",
+ "\n",
+ "# Propagate the state\n",
+ "propagate_state(m.fs.s11)\n",
+ "\n",
+ "# Fix the reflux ratio, boilup ratio, and the condenser pressure\n",
+ "m.fs.D101.condenser.reflux_ratio.fix(0.5)\n",
+ "m.fs.D101.reboiler.boilup_ratio.fix(0.5)\n",
+ "m.fs.D101.condenser.condenser_pressure.fix(150000)\n",
+ "\n",
+ "# set scaling factors\n",
+ "# Set scaling factors for heat duty\n",
+ "iscale.set_scaling_factor(m.fs.D101.condenser.control_volume.heat, 1e-2)\n",
+ "iscale.set_scaling_factor(m.fs.D101.reboiler.control_volume.heat, 1e-2)\n",
+ "\n",
+ "# Set the scaling factors for the remaining variables and all constraints\n",
+ "iscale.calculate_scaling_factors(m.fs.D101)\n",
+ "\n",
+ "# Initialize the distillation column\n",
+ "m.fs.D101.initialize(outlvl=idaeslog.INFO)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Adding expressions to compute capital and operating costs\n",
+ "\n",
+ "In this section, we will add a few Expressions that allow us to evaluate the performance. Expressions provide a convenient way of calculating certain values that are a function of the variables defined in the model. For more details on Expressions, please refer to: https://pyomo.readthedocs.io/en/stable/pyomo_modeling_components/Expressions.html"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 51,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Expression to compute the total cooling cost\n",
+ "m.fs.cooling_cost = Expression(\n",
+ " expr=0.25e-7 * (-m.fs.F101.heat_duty[0])\n",
+ " + 0.2e-7 * (-m.fs.D101.condenser.heat_duty[0])\n",
+ ")\n",
+ "\n",
+ "# Expression to compute the total heating cost\n",
+ "m.fs.heating_cost = Expression(\n",
+ " expr=2.2e-7 * m.fs.H101.heat_duty[0]\n",
+ " + 1.2e-7 * m.fs.H102.heat_duty[0]\n",
+ " + 1.9e-7 * m.fs.D101.reboiler.heat_duty[0]\n",
+ ")\n",
+ "\n",
+ "# Expression to compute the total operating cost\n",
+ "m.fs.operating_cost = Expression(\n",
+ " expr=(3600 * 24 * 365 * (m.fs.heating_cost + m.fs.cooling_cost))\n",
+ ")\n",
+ "\n",
+ "# Expression to compute the total capital cost\n",
+ "m.fs.capital_cost = Expression(expr=1e5 * m.fs.R101.volume[0])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Solve the entire flowsheet"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 53,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "WARNING: model contains export suffix 'scaling_factor' that contains 7\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 150 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "Ipopt 3.13.2: nlp_scaling_method=gradient-based\n",
+ "tol=1e-06\n",
+ "max_iter=200\n",
+ "\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...: 4042\n",
+ "Number of nonzeros in inequality constraint Jacobian.: 0\n",
+ "Number of nonzeros in Lagrangian Hessian.............: 2376\n",
+ "\n",
+ "Total number of variables............................: 1169\n",
+ " variables with only lower bounds: 112\n",
+ " variables with lower and upper bounds: 365\n",
+ " variables with only upper bounds: 0\n",
+ "Total number of equality constraints.................: 1169\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.83e+04 1.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
+ " 1 0.0000000e+00 8.70e+03 1.50e+03 -1.0 3.69e+04 - 9.71e-01 4.62e-01H 1\n",
+ " 2 0.0000000e+00 1.53e+03 1.56e+03 -1.0 6.75e+03 - 9.77e-01 4.89e-01h 1\n",
+ " 3 0.0000000e+00 1.37e+03 1.55e+05 -1.0 9.37e+03 - 9.90e-01 4.99e-01h 1\n",
+ " 4 0.0000000e+00 6.14e+02 2.31e+09 -1.0 6.09e+03 - 1.00e+00 9.81e-01h 1\n",
+ " 5 0.0000000e+00 5.32e+03 3.62e+10 -1.0 5.56e+02 - 1.00e+00 9.90e-01h 1\n",
+ " 6 0.0000000e+00 1.16e+03 7.80e+09 -1.0 5.36e+00 - 1.00e+00 1.00e+00h 1\n",
+ " 7 0.0000000e+00 5.96e+00 3.64e+07 -1.0 1.47e-03 - 1.00e+00 1.00e+00f 1\n",
+ " 8 0.0000000e+00 1.69e-04 8.15e+02 -1.0 6.77e-06 - 1.00e+00 1.00e+00h 1\n",
+ " 9 0.0000000e+00 7.45e-09 6.64e-03 -3.8 2.00e-07 - 1.00e+00 1.00e+00h 1\n",
+ "Cannot recompute multipliers for feasibility problem. Error in eq_mult_calculator\n",
+ "\n",
+ "Number of Iterations....: 9\n",
+ "\n",
+ " (scaled) (unscaled)\n",
+ "Objective...............: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Dual infeasibility......: 1.5042483516409773e+04 1.5042483516409773e+04\n",
+ "Constraint violation....: 2.9103830456733704e-11 7.4505805969238281e-09\n",
+ "Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Overall NLP error.......: 2.9103830456733704e-11 1.5042483516409773e+04\n",
+ "\n",
+ "\n",
+ "Number of objective function evaluations = 11\n",
+ "Number of objective gradient evaluations = 10\n",
+ "Number of equality constraint evaluations = 11\n",
+ "Number of inequality constraint evaluations = 0\n",
+ "Number of equality constraint Jacobian evaluations = 10\n",
+ "Number of inequality constraint Jacobian evaluations = 0\n",
+ "Number of Lagrangian Hessian evaluations = 9\n",
+ "Total CPU secs in IPOPT (w/o function evaluations) = 0.083\n",
+ "Total CPU secs in NLP function evaluations = 0.013\n",
+ "\n",
+ "EXIT: Optimal Solution Found.\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "{'Problem': [{'Lower bound': -inf, 'Upper bound': inf, 'Number of objectives': 1, 'Number of constraints': 1169, 'Number of variables': 1169, 'Sense': 'unknown'}], 'Solver': [{'Status': 'ok', 'Message': 'Ipopt 3.13.2\\\\x3a Optimal Solution Found', 'Termination condition': 'optimal', 'Id': 0, 'Error rc': 0, 'Time': 0.2022566795349121}], 'Solution': [OrderedDict([('number of solutions', 0), ('number of solutions displayed', 0)])]}"
+ ]
+ },
+ "execution_count": 53,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "solver.solve(m, tee=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Analyze the Results of the Square Problem\n",
+ "\n",
+ "How much is the total cost (operating cost + capital cost), operating cost, capital cost, benzene purity in the distillate from the distilation column, and conversion of toluene in the reactor?"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 55,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "total cost = $ 442301.47075252194\n",
+ "operating cost = $ 427596.73056805483\n",
+ "capital cost = $ 14704.740184467111\n",
+ "\n",
+ "Distillate flowrate = 0.16196898920633368 mol/s\n",
+ "Benzene purity = 89.4916166580088 %\n",
+ "Residue flowrate = 0.10515007120697904 mol/s\n",
+ "Toluene purity = 43.32260291055251 %\n",
+ "\n",
+ "Conversion = 75.0 %\n",
+ "\n",
+ "Overhead benzene loss in F101 = 42.161938483603194 %\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(\"total cost = $\", value(m.fs.capital_cost) + value(m.fs.operating_cost))\n",
+ "print(\"operating cost = $\", value(m.fs.operating_cost))\n",
+ "print(\"capital cost = $\", value(m.fs.capital_cost))\n",
+ "print()\n",
+ "print(\n",
+ " \"Distillate flowrate = \",\n",
+ " value(m.fs.D101.condenser.distillate.flow_mol[0]()),\n",
+ " \"mol/s\",\n",
+ ")\n",
+ "print(\n",
+ " \"Benzene purity = \",\n",
+ " 100 * value(m.fs.D101.condenser.distillate.mole_frac_comp[0, \"benzene\"]),\n",
+ " \"%\",\n",
+ ")\n",
+ "print(\"Residue flowrate = \", value(m.fs.D101.reboiler.bottoms.flow_mol[0]()), \"mol/s\")\n",
+ "print(\n",
+ " \"Toluene purity = \",\n",
+ " 100 * value(m.fs.D101.reboiler.bottoms.mole_frac_comp[0, \"toluene\"]),\n",
+ " \"%\",\n",
+ ")\n",
+ "print()\n",
+ "print(\"Conversion = \", 100 * value(m.fs.R101.conversion), \"%\")\n",
+ "print()\n",
+ "print(\n",
+ " \"Overhead benzene loss in F101 = \",\n",
+ " 100\n",
+ " * value(m.fs.F101.vap_outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"])\n",
+ " / value(m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]),\n",
+ " \"%\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Get the state of the streams entering and leaving the reactor R101"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 57,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "====================================================================================\n",
+ "Unit : fs.R101 Time: 0.0\n",
+ "------------------------------------------------------------------------------------\n",
+ " Unit Performance\n",
+ "\n",
+ " Variables: \n",
+ "\n",
+ " Key : Value : Units : Fixed : Bounds\n",
+ " Heat Duty : 0.0000 : watt : True : (None, None)\n",
+ " Volume : 0.14705 : meter ** 3 : False : (None, None)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ " Stream Table\n",
+ " Units Inlet Outlet \n",
+ " flow_mol_phase_comp ('Liq', 'benzene') mole / second 1.2993e-07 1.2993e-07\n",
+ " flow_mol_phase_comp ('Liq', 'toluene') mole / second 8.4147e-07 8.4147e-07\n",
+ " flow_mol_phase_comp ('Liq', 'methane') mole / second 1.0000e-12 1.0000e-12\n",
+ " flow_mol_phase_comp ('Liq', 'hydrogen') mole / second 1.0000e-12 1.0000e-12\n",
+ " flow_mol_phase_comp ('Vap', 'benzene') mole / second 0.11936 0.35374\n",
+ " flow_mol_phase_comp ('Vap', 'toluene') mole / second 0.31252 0.078129\n",
+ " flow_mol_phase_comp ('Vap', 'methane') mole / second 1.0377 1.2721\n",
+ " flow_mol_phase_comp ('Vap', 'hydrogen') mole / second 0.56260 0.32821\n",
+ " temperature kelvin 600.00 771.85\n",
+ " pressure pascal 3.5000e+05 3.5000e+05\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "m.fs.R101.report()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Get the state of the streams entering and leaving the reactor R101"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 58,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "====================================================================================\n",
+ "Unit : fs.F101 Time: 0.0\n",
+ "------------------------------------------------------------------------------------\n",
+ " Unit Performance\n",
+ "\n",
+ " Variables: \n",
+ "\n",
+ " Key : Value : Units : Fixed : Bounds\n",
+ " Heat Duty : -70343. : watt : False : (None, None)\n",
+ " Pressure Change : 0.0000 : pascal : True : (None, None)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ " Stream Table\n",
+ " Units Inlet Vapor Outlet Liquid Outlet\n",
+ " flow_mol_phase_comp ('Liq', 'benzene') mole / second 1.2993e-07 1.0000e-08 0.20460 \n",
+ " flow_mol_phase_comp ('Liq', 'toluene') mole / second 8.4147e-07 1.0000e-08 0.062520 \n",
+ " flow_mol_phase_comp ('Liq', 'methane') mole / second 1.0000e-12 1.0000e-08 2.6712e-07 \n",
+ " flow_mol_phase_comp ('Liq', 'hydrogen') mole / second 1.0000e-12 1.0000e-08 2.6712e-07 \n",
+ " flow_mol_phase_comp ('Vap', 'benzene') mole / second 0.35374 0.14915 1.0000e-08 \n",
+ " flow_mol_phase_comp ('Vap', 'toluene') mole / second 0.078129 0.015610 1.0000e-08 \n",
+ " flow_mol_phase_comp ('Vap', 'methane') mole / second 1.2721 1.2721 1.0000e-08 \n",
+ " flow_mol_phase_comp ('Vap', 'hydrogen') mole / second 0.32821 0.32821 1.0000e-08 \n",
+ " temperature kelvin 771.85 325.00 325.00 \n",
+ " pressure pascal 3.5000e+05 3.5000e+05 3.5000e+05 \n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "m.fs.F101.report()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Next, let's look at how much benzene we are losing with the light gases out of F101. IDAES has tools for creating stream tables based on the `Arcs` and/or `Ports` in a flowsheet. Let us create and print a simple stream table showing the stream leaving the reactor and the vapor stream from F101.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "How much benzene are we losing in the F101 vapor outlet stream?\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 59,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ " Units Reactor Light Gases\n",
+ "flow_mol_phase_comp ('Liq', 'benzene') mole / second 1.2993e-07 1.0000e-08 \n",
+ "flow_mol_phase_comp ('Liq', 'toluene') mole / second 8.4147e-07 1.0000e-08 \n",
+ "flow_mol_phase_comp ('Liq', 'methane') mole / second 1.0000e-12 1.0000e-08 \n",
+ "flow_mol_phase_comp ('Liq', 'hydrogen') mole / second 1.0000e-12 1.0000e-08 \n",
+ "flow_mol_phase_comp ('Vap', 'benzene') mole / second 0.35374 0.14915 \n",
+ "flow_mol_phase_comp ('Vap', 'toluene') mole / second 0.078129 0.015610 \n",
+ "flow_mol_phase_comp ('Vap', 'methane') mole / second 1.2721 1.2721 \n",
+ "flow_mol_phase_comp ('Vap', 'hydrogen') mole / second 0.32821 0.32821 \n",
+ "temperature kelvin 771.85 325.00 \n",
+ "pressure pascal 3.5000e+05 3.5000e+05 \n"
+ ]
+ }
+ ],
+ "source": [
+ "from idaes.core.util.tables import (\n",
+ " create_stream_table_dataframe,\n",
+ " stream_table_dataframe_to_string,\n",
+ ")\n",
+ "\n",
+ "st = create_stream_table_dataframe({\"Reactor\": m.fs.s05, \"Light Gases\": m.fs.s06})\n",
+ "print(stream_table_dataframe_to_string(st))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "You can query additional variables here if you like. \n",
+ "\n",
+ "Use Shift+Enter to run the cell once you have typed in your code. \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Optimization\n",
+ "\n",
+ "\n",
+ "We saw from the results above that the total operating cost for the base case was $442,297 per year. We are producing 0.162 mol/s of benzene at a purity of 89.5%. However, we are losing around 43.3% of benzene in F101 vapor outlet stream. \n",
+ "\n",
+ "Let us try to minimize this cost such that:\n",
+ "- we are producing at least 0.18 mol/s of benzene as distillate i.e. our product stream\n",
+ "- purity of benzene i.e. the mole fraction of benzene in the distillate is at least 99%\n",
+ "- restricting the benzene loss in F101 vapor outlet to less than 20%\n",
+ "\n",
+ "For this problem, our decision variables are as follows:\n",
+ "- H101 outlet temperature\n",
+ "- R101 outlet temperature\n",
+ "- F101 outlet temperature\n",
+ "- H102 outlet temperature\n",
+ "- Condenser pressure\n",
+ "- reflux ratio\n",
+ "- boilup ratio\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let us declare our objective function for this problem. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 60,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.fs.objective = Objective(expr=m.fs.operating_cost + m.fs.capital_cost)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now, we need to unfix the decision variables as we had solved a square problem (degrees of freedom = 0) until now. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 61,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.fs.H101.outlet.temperature.unfix()\n",
+ "m.fs.R101.conversion.unfix()\n",
+ "m.fs.F101.vap_outlet.temperature.unfix()\n",
+ "m.fs.D101.condenser.condenser_pressure.unfix()\n",
+ "m.fs.D101.condenser.reflux_ratio.unfix()\n",
+ "m.fs.D101.reboiler.boilup_ratio.unfix()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "Let us now unfix the remaining variable: the temperature of the outlet from H102\n",
+ "\n",
+ "Use Shift+Enter to run the cell once you have typed in your code. \n",
+ "
\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 62,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Unfix the temperature of the outlet from H102"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 63,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Unfix the temperature of the outlet from H102\n",
+ "m.fs.H102.outlet.temperature.unfix()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Next, we need to set bounds on these decision variables to values shown below:\n",
+ "\n",
+ " - H101 outlet temperature [500, 600] K\n",
+ " - R101 outlet temperature [600, 900] K\n",
+ " - F101 outlet temperature [298, 450] K\n",
+ " - H102 outlet temperature [350, 400] K\n",
+ " - D101 condenser pressure [101325, 150000] Pa\n",
+ " - D101 reflux ratio [0.1, 5]\n",
+ " - D101 boilup ratio [0.1, 5]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 64,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Set bounds on the temperature of the outlet from H101\n",
+ "m.fs.H101.outlet.temperature[0].setlb(500)\n",
+ "m.fs.H101.outlet.temperature[0].setub(600)\n",
+ "\n",
+ "# Set bounds on the temperature of the outlet from R101\n",
+ "m.fs.R101.outlet.temperature[0].setlb(600)\n",
+ "m.fs.R101.outlet.temperature[0].setub(900)\n",
+ "\n",
+ "# Set bounds on the volume of the reactor R101\n",
+ "m.fs.R101.volume[0].setlb(0)\n",
+ "\n",
+ "# Set bounds on the temperature of the vapor outlet from F101\n",
+ "m.fs.F101.vap_outlet.temperature[0].setlb(298)\n",
+ "m.fs.F101.vap_outlet.temperature[0].setub(450.0)\n",
+ "\n",
+ "# Set bounds on the temperature of the outlet from H102\n",
+ "m.fs.H102.outlet.temperature[0].setlb(350)\n",
+ "m.fs.H102.outlet.temperature[0].setub(400)\n",
+ "\n",
+ "# Set bounds on the pressure inside the condenser\n",
+ "m.fs.D101.condenser.condenser_pressure.setlb(101325)\n",
+ "m.fs.D101.condenser.condenser_pressure.setub(150000)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "Now, set the bounds for the D101 reflux ratio and boilup ratio.\n",
+ "\n",
+ "Use Shift+Enter to run the cell once you have typed in your code. \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 65,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Set bounds on the reflux ratio\n",
+ "\n",
+ "\n",
+ "# Todo: Set bounds on the boilup ratio"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 66,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Set bounds on the reflux ratio\n",
+ "m.fs.D101.condenser.reflux_ratio.setlb(0.1)\n",
+ "m.fs.D101.condenser.reflux_ratio.setub(5)\n",
+ "\n",
+ "# Todo: Set bounds on the boilup ratio\n",
+ "m.fs.D101.reboiler.boilup_ratio.setlb(0.1)\n",
+ "m.fs.D101.reboiler.boilup_ratio.setub(5)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now, the only things left to define are our constraints on overhead loss in F101, distillate flowrate and its purity. Let us first look at defining a constraint for the overhead loss in F101 where we are restricting the benzene leaving the vapor stream to less than 20 % of the benzene available in the reactor outlet. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 67,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Ensure that the overhead loss of benzene from F101 <= 20%\n",
+ "m.fs.overhead_loss = Constraint(\n",
+ " expr=m.fs.F101.vap_outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]\n",
+ " <= 0.20 * m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "Now, add the constraint such that we are producing at least 0.18 mol/s of benzene in the product stream which is the distillate of D101. Let us name this constraint as m.fs.product_flow. \n",
+ "\n",
+ "Use Shift+Enter to run the cell once you have typed in your code. \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 68,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Add minimum product flow constraint"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 69,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Add minimum product flow constraint\n",
+ "m.fs.product_flow = Constraint(expr=m.fs.D101.condenser.distillate.flow_mol[0] >= 0.18)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let us add the final constraint on product purity or the mole fraction of benzene in the distillate such that it is at least greater than 99%. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 70,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.fs.product_purity = Constraint(\n",
+ " expr=m.fs.D101.condenser.distillate.mole_frac_comp[0, \"benzene\"] >= 0.99\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "We have now defined the optimization problem and we are now ready to solve this problem. \n",
+ "\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 71,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "WARNING: model contains export suffix 'scaling_factor' that contains 3\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 150 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "Ipopt 3.13.2: nlp_scaling_method=gradient-based\n",
+ "tol=1e-06\n",
+ "max_iter=200\n",
+ "\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...: 4073\n",
+ "Number of nonzeros in inequality constraint Jacobian.: 6\n",
+ "Number of nonzeros in Lagrangian Hessian.............: 2391\n",
+ "\n",
+ "Total number of variables............................: 1176\n",
+ " variables with only lower bounds: 113\n",
+ " variables with lower and upper bounds: 372\n",
+ " variables with only upper bounds: 0\n",
+ "Total number of equality constraints.................: 1169\n",
+ "Total number of inequality constraints...............: 3\n",
+ " inequality constraints with only lower bounds: 2\n",
+ " inequality constraints with lower and upper bounds: 0\n",
+ " inequality constraints with only upper bounds: 1\n",
+ "\n",
+ "iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n",
+ " 0 4.4230147e+05 2.99e+05 9.90e+01 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
+ " 1 4.3753585e+05 2.91e+05 1.28e+02 -1.0 3.09e+06 - 3.58e-01 2.40e-02f 1\n",
+ " 2 4.3545100e+05 2.78e+05 1.55e+02 -1.0 1.78e+06 - 3.31e-01 4.74e-02h 1\n",
+ " 3 4.2822311e+05 2.20e+05 4.50e+02 -1.0 2.99e+06 - 2.95e-02 1.35e-01h 1\n",
+ " 4 4.2249096e+05 1.45e+05 1.43e+03 -1.0 7.01e+06 - 5.14e-01 2.03e-01h 1\n",
+ " 5 4.2194364e+05 8.17e+04 1.70e+04 -1.0 6.06e+06 - 5.97e-01 4.28e-01h 1\n",
+ " 6 4.2602765e+05 4.55e+04 1.10e+06 -1.0 4.32e+06 - 9.26e-01 5.07e-01h 1\n",
+ " 7 4.3776643e+05 2.03e+04 6.44e+09 -1.0 2.42e+06 - 9.90e-01 9.47e-01h 1\n",
+ " 8 4.3846260e+05 1.92e+04 6.05e+09 -1.0 4.42e+05 - 5.40e-01 5.74e-02h 1\n",
+ " 9 4.4529853e+05 4.05e+04 4.66e+10 -1.0 2.47e+05 - 9.96e-01 9.90e-01h 1\n",
+ "iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n",
+ " 10 4.4906283e+05 9.76e+03 1.10e+10 -1.0 1.12e+03 -4.0 1.26e-01 7.45e-01h 1\n",
+ " 11 4.5079086e+05 1.19e+03 1.54e+09 -1.0 5.63e+02 -4.5 3.77e-01 1.00e+00h 1\n",
+ " 12 4.5024224e+05 2.66e+00 3.67e+06 -1.0 6.61e+01 -5.0 1.00e+00 1.00e+00f 1\n",
+ " 13 4.4946170e+05 5.64e-01 9.29e+05 -1.0 1.81e+02 -5.4 1.00e+00 7.88e-01f 1\n",
+ " 14 4.4916780e+05 8.48e+00 1.62e+05 -1.0 2.83e+02 -5.9 1.00e+00 1.00e+00f 1\n",
+ " 15 4.4899127e+05 4.83e+00 9.07e+04 -1.0 1.01e+02 -6.4 1.00e+00 4.40e-01f 2\n",
+ " 16 4.4886718e+05 7.00e-01 4.61e+02 -1.0 2.35e+02 -6.9 1.00e+00 1.00e+00f 1\n",
+ " 17 4.4800159e+05 1.39e+02 4.52e+06 -3.8 1.17e+03 -7.3 9.79e-01 9.37e-01f 1\n",
+ " 18 4.4672196e+05 9.59e+02 1.22e+06 -3.8 4.55e+03 -7.8 1.00e+00 9.43e-01f 1\n",
+ " 19 4.4401667e+05 7.75e+03 1.55e+05 -3.8 1.08e+04 -8.3 1.00e+00 1.00e+00f 1\n",
+ "iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n",
+ " 20 4.4185035e+05 1.91e+04 1.36e+04 -3.8 1.33e+04 -8.8 1.00e+00 1.00e+00h 1\n",
+ " 21 4.4241001e+05 3.52e+03 5.96e+03 -3.8 2.94e+03 -9.2 1.00e+00 1.00e+00h 1\n",
+ " 22 4.4185237e+05 7.82e+00 2.91e+02 -3.8 7.13e+03 -9.7 2.39e-01 1.00e+00h 1\n",
+ " 23 4.4124091e+05 1.53e+01 3.11e+02 -3.8 4.82e+04 -10.2 8.59e-01 1.36e-01f 1\n",
+ " 24 4.4137379e+05 1.80e+00 2.91e+02 -3.8 1.41e+04 - 1.95e-01 1.00e+00h 1\n",
+ " 25 4.3862833e+05 1.70e+03 9.48e+04 -3.8 1.57e+07 - 1.29e-03 9.10e-02f 1\n",
+ " 26 4.3883308e+05 1.49e+03 8.46e+04 -3.8 1.02e+06 - 1.00e+00 1.35e-01h 1\n",
+ " 27 4.3885472e+05 2.18e+01 3.40e+03 -3.8 1.38e+05 - 1.00e+00 1.00e+00h 1\n",
+ " 28 4.3884160e+05 5.90e-02 6.38e+01 -3.8 8.66e+03 - 1.00e+00 1.00e+00h 1\n",
+ " 29 4.3884157e+05 6.48e-07 4.63e-04 -3.8 2.89e+01 - 1.00e+00 1.00e+00h 1\n",
+ "iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n",
+ " 30 4.3883990e+05 3.57e-01 2.38e+03 -5.7 8.19e+02 - 1.00e+00 1.00e+00f 1\n",
+ " 31 4.3883992e+05 3.50e-07 7.79e-06 -5.7 3.55e-01 - 1.00e+00 1.00e+00h 1\n",
+ " 32 4.3883990e+05 5.47e-05 3.63e-01 -8.0 1.01e+01 - 1.00e+00 1.00e+00h 1\n",
+ " 33 4.3883990e+05 2.24e-08 1.46e-07 -8.0 5.42e-05 - 1.00e+00 1.00e+00h 1\n",
+ "\n",
+ "Number of Iterations....: 33\n",
+ "\n",
+ " (scaled) (unscaled)\n",
+ "Objective...............: 4.3883989842628603e+02 4.3883989842628600e+05\n",
+ "Dual infeasibility......: 1.4600704448671754e-07 1.4600704448671753e-04\n",
+ "Constraint violation....: 2.9103830456733704e-11 2.2351741790771484e-08\n",
+ "Complementarity.........: 9.0909948039799681e-09 9.0909948039799686e-06\n",
+ "Overall NLP error.......: 9.0909948039799681e-09 1.4600704448671753e-04\n",
+ "\n",
+ "\n",
+ "Number of objective function evaluations = 35\n",
+ "Number of objective gradient evaluations = 34\n",
+ "Number of equality constraint evaluations = 35\n",
+ "Number of inequality constraint evaluations = 35\n",
+ "Number of equality constraint Jacobian evaluations = 34\n",
+ "Number of inequality constraint Jacobian evaluations = 34\n",
+ "Number of Lagrangian Hessian evaluations = 33\n",
+ "Total CPU secs in IPOPT (w/o function evaluations) = 0.164\n",
+ "Total CPU secs in NLP function evaluations = 0.020\n",
+ "\n",
+ "EXIT: Optimal Solution Found.\n"
+ ]
+ }
+ ],
+ "source": [
+ "results = solver.solve(m, tee=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Optimization Results\n",
+ "\n",
+ "Display the results and product specifications"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 73,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "total cost = $ 438839.898426286\n",
+ "operating cost = $ 408883.5314830889\n",
+ "capital cost = $ 29956.3669431971\n",
+ "\n",
+ "Distillate flowrate = 0.1799999900263989 mol/s\n",
+ "Benzene purity = 98.99999900049086 %\n",
+ "Residue flowrate = 0.1085161642426372 mol/s\n",
+ "Toluene purity = 15.676178086213548 %\n",
+ "\n",
+ "Conversion = 93.38705916369427 %\n",
+ "\n",
+ "Overhead benzene loss in F101 = 17.34061793115618 %\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(\"total cost = $\", value(m.fs.capital_cost) + value(m.fs.operating_cost))\n",
+ "print(\"operating cost = $\", value(m.fs.operating_cost))\n",
+ "print(\"capital cost = $\", value(m.fs.capital_cost))\n",
+ "print()\n",
+ "print(\n",
+ " \"Distillate flowrate = \",\n",
+ " value(m.fs.D101.condenser.distillate.flow_mol[0]()),\n",
+ " \"mol/s\",\n",
+ ")\n",
+ "print(\n",
+ " \"Benzene purity = \",\n",
+ " 100 * value(m.fs.D101.condenser.distillate.mole_frac_comp[0, \"benzene\"]),\n",
+ " \"%\",\n",
+ ")\n",
+ "print(\"Residue flowrate = \", value(m.fs.D101.reboiler.bottoms.flow_mol[0]()), \"mol/s\")\n",
+ "print(\n",
+ " \"Toluene purity = \",\n",
+ " 100 * value(m.fs.D101.reboiler.bottoms.mole_frac_comp[0, \"toluene\"]),\n",
+ " \"%\",\n",
+ ")\n",
+ "print()\n",
+ "print(\"Conversion = \", 100 * value(m.fs.R101.conversion), \"%\")\n",
+ "print()\n",
+ "print(\n",
+ " \"Overhead benzene loss in F101 = \",\n",
+ " 100\n",
+ " * value(m.fs.F101.vap_outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"])\n",
+ " / value(m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]),\n",
+ " \"%\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Display optimal values for the decision variables"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 75,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Optimal Values\n",
+ "\n",
+ "H101 outlet temperature = 568.923204295196 K\n",
+ "\n",
+ "R101 outlet temperature = 790.3655425698853 K\n",
+ "\n",
+ "F101 outlet temperature = 298.0 K\n",
+ "\n",
+ "H102 outlet temperature = 368.7414339952852 K\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(\"Optimal Values\")\n",
+ "print()\n",
+ "\n",
+ "print(\"H101 outlet temperature = \", value(m.fs.H101.outlet.temperature[0]), \"K\")\n",
+ "\n",
+ "print()\n",
+ "print(\"R101 outlet temperature = \", value(m.fs.R101.outlet.temperature[0]), \"K\")\n",
+ "\n",
+ "print()\n",
+ "print(\"F101 outlet temperature = \", value(m.fs.F101.vap_outlet.temperature[0]), \"K\")\n",
+ "\n",
+ "print()\n",
+ "print(\"H102 outlet temperature = \", value(m.fs.H102.outlet.temperature[0]), \"K\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Key Takeaways\n",
+ "\n",
+ "Observe that the optimization was able to reduce the yearly operating cost from \\\\$427,593 to \\\\$408,342 (~4.5%). However, the amortized capital cost more than doubled from \\\\$14,704 to \\\\$29,927 due to the need to increase the conversion in the reactor (from 75% to 93%) to meet the production and purity constraints. \n",
+ "\n",
+ "Further, observe that the product flow rate and product purity are at their minimum values (0.18 mol/s and 99%, respectively). This is expected as increasing recovery would require more energy and cost to purify the product.\n",
+ "\n",
+ "\n",
+ "Finally, observe that the operating temperature of the flash (F101) is almost at its lower bound. This helps in minimizing the amount of benzene in the vapor stream leaving the flash."
+ ]
+ }
+ ],
+ "metadata": {
+ "celltoolbar": "Tags",
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
},
- "nbformat": 4,
- "nbformat_minor": 3
+ "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.10.13"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 3
}
\ No newline at end of file
diff --git a/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_distillation_test.ipynb b/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_distillation_test.ipynb
index 05b005f1..ba91dc8e 100644
--- a/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_distillation_test.ipynb
+++ b/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_distillation_test.ipynb
@@ -1,1782 +1,3033 @@
{
- "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": [
- "\n",
- "# HDA Flowsheet Simulation and Optimization\n",
- "Maintainer: Brandon Paul \n",
- "Author: Brandon Paul \n",
- "Updated: 2023-06-01 \n",
- "\n",
- "\n",
- "## Note\n",
- "\n",
- "This tutorial will be similar to the HDA flowsheet tutorial in the Tutorials section, except that we use a distillation column instead of a second flash (F102) to produce benzene and toluene products.\n",
- "\n",
- "\n",
- "## Learning outcomes\n",
- "\n",
- "\n",
- "- Construct a steady-state flowsheet using the IDAES unit model library\n",
- "- Connecting unit models in a flowsheet using Arcs\n",
- "- Using the SequentialDecomposition tool to initialize a flowsheet with recycle\n",
- "- Fomulate and solve an optimization problem\n",
- " - Defining an objective function\n",
- " - Setting variable bounds\n",
- " - Adding additional constraints \n",
- "\n",
- "\n",
- "## Problem Statement\n",
- "\n",
- "Hydrodealkylation is a chemical reaction that often involves reacting\n",
- "an aromatic hydrocarbon in the presence of hydrogen gas to form a\n",
- "simpler aromatic hydrocarbon devoid of functional groups. In this\n",
- "example, toluene will be reacted with hydrogen gas at high temperatures\n",
- " to form benzene via the following reaction:\n",
- "\n",
- "**C6H5CH3 + H2 \u2192 C6H6 + CH4**\n",
- "\n",
- "\n",
- "This reaction is often accompanied by an equilibrium side reaction\n",
- "which forms diphenyl, which we will neglect for this example.\n",
- "\n",
- "This example is based on the 1967 AIChE Student Contest problem as\n",
- "present by Douglas, J.M., Chemical Design of Chemical Processes, 1988,\n",
- "McGraw-Hill.\n",
- "\n",
- "The flowsheet that we will be using for this module is shown below with the stream conditions. We will be processing toluene and hydrogen to produce at least 370 TPY of benzene. As shown in the flowsheet, we use a flash tank, F101, to separate out the non-condensibles, and a distillation column, D101, to further separate the benzene-toluene mixture to improve the benzene purity. The non-condensibles separated out in F101 will be partially recycled back to M101 and the rest will be purged. We will assume ideal gas behavior for this flowsheet. The properties required for this module are defined in\n",
- "\n",
- "- `hda_ideal_VLE.py`\n",
- "- `idaes.models.properties.activity_coeff_models.BTX_activity_coeff_VLE`\n",
- "- `hda_reaction.py`\n",
- "\n",
- "We will be using two thermodynamic packages: one (first in the list above) containing all four components (i.e., toluene, hydrogen, benzene, and methane) and the other (second in the list above) containing benzene and toluene only. The latter is needed to simplify the VLE calculations in the distillation column model. \n",
- "\n",
- "![](HDA_flowsheet_distillation.png)\n",
- "\n",
- ""
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Translator block\n",
- "\n",
- "Benzene and toluene are separated by distillation, so the process involves phase equilibrium and two-phase flow conditions. However, the presence of hydrogen and methane complicates the calculations. This is because, hydrogen and methane are non-condensable under all conditions of interest; ergo, a vapor phase will always be present, and the mixture bubble point is extremely low. To simplify the phase equilibrium calculations, hydrogen and methane will be considered completely as non-condensable and insoluble in the liquid outlet from the flash F101.\n",
- "\n",
- "Since no hydrogen and methane will be present in the unit operations following the flash, a different component list can be used to simplify the property calculations. IDAES supports the definition of multiple property packages within a single flowsheet via `Translator` blocks. `Translator` blocks convert between different property calculations, component lists, and equations of state. "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Importing required pyomo and idaes components\n",
- "\n",
- "\n",
- "To construct a flowsheet, we will need several components from the pyomo and idaes package. Let us first import the following components from Pyomo:\n",
- "- Constraint (to write constraints)\n",
- "- Var (to declare variables)\n",
- "- ConcreteModel (to create the concrete model object)\n",
- "- Expression (to evaluate values as a function of variables defined in the model)\n",
- "- Objective (to define an objective function for optimization)\n",
- "- SolverFactory (to solve the problem)\n",
- "- TransformationFactory (to apply certain transformations)\n",
- "- Arc (to connect two unit models)\n",
- "- SequentialDecomposition (to initialize the flowsheet in a sequential mode)\n",
- "\n",
- "For further details on these components, please refer to the pyomo documentation: https://pyomo.readthedocs.io/en/stable/\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "from pyomo.environ import (\n",
- " Constraint,\n",
- " Var,\n",
- " ConcreteModel,\n",
- " Expression,\n",
- " Objective,\n",
- " TransformationFactory,\n",
- " value,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "Import `Arc` and `SequentialDecomposition` tools from `pyomo.network`\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Import the above mentioned tools from pyomo.network\n",
- "from pyomo.network import Arc, SequentialDecomposition"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "From IDAES, we will be needing the FlowsheetBlock and the following unit models:\n",
- "- Mixer\n",
- "- Heater\n",
- "- CSTR\n",
- "- Flash\n",
- "- Separator (splitter) \n",
- "- PressureChanger\n",
- "- Translator (to switch from one property package to another)\n",
- "- TrayColumn (distillation column)\n",
- "- CondenserType (Type of the overhead condenser: complete or partial)\n",
- "- TemperatureSpec (Temperature specification inside the condenser)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [],
- "source": [
- "from idaes.core import FlowsheetBlock"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {},
- "outputs": [],
- "source": [
- "from idaes.models.unit_models import (\n",
- " PressureChanger,\n",
- " Mixer,\n",
- " Separator as Splitter,\n",
- " Heater,\n",
- " CSTR,\n",
- " Flash,\n",
- " Translator,\n",
- ")\n",
- "\n",
- "from idaes.models_extra.column_models import TrayColumn\n",
- "from idaes.models_extra.column_models.condenser import CondenserType, TemperatureSpec"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We will also be needing some utility tools to put together the flowsheet and calculate the degrees of freedom. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Utility tools to put together the flowsheet and calculate the degrees of freedom\n",
- "from idaes.models.unit_models.pressure_changer import ThermodynamicAssumption\n",
- "from idaes.core.util.model_statistics import degrees_of_freedom\n",
- "from idaes.core.util.initialization import propagate_state\n",
- "from idaes.core.solvers import get_solver\n",
- "import idaes.core.util.scaling as iscale\n",
- "\n",
- "# Import idaes logger to set output levels\n",
- "import idaes.logger as idaeslog"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Importing required thermo and reaction packages\n",
- "\n",
- "Finally, we import the thermophysical (`ideal_VLE.py` and `BTXParameterBlock`) packages and reaction package (`reaction.py`) for the HDA process. We have created custom thermophysical packages that assume ideal gas behavior with support for VLE. The reaction package consists of the stochiometric coefficients for the reaction, heat of reaction, and kinetic information (Arrhenius constant and activation energy). "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "metadata": {},
- "outputs": [],
- "source": [
- "from idaes_examples.mod.hda import hda_reaction as reaction_props\n",
- "from idaes.models.properties.activity_coeff_models.BTX_activity_coeff_VLE import (\n",
- " BTXParameterBlock,\n",
- ")\n",
- "\n",
- "from idaes_examples.mod.hda.hda_ideal_VLE import HDAParameterBlock"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Constructing the Flowsheet\n",
- "\n",
- "We have now imported all the components, unit models, and property modules we need to construct a flowsheet. Let us create a ConcreteModel and add the flowsheet block to it. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Create a Pyomo Concrete Model to contain the problem\n",
- "m = ConcreteModel()\n",
- "\n",
- "# Add a steady state flowsheet block to the model\n",
- "m.fs = FlowsheetBlock(dynamic=False)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We will now add the thermophysical and reaction packages to the flowsheet."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Property package for benzene, toluene, hydrogen, methane mixture\n",
- "m.fs.BTHM_params = HDAParameterBlock()\n",
- "\n",
- "# Property package for the benzene-toluene mixture\n",
- "m.fs.BT_params = BTXParameterBlock(\n",
- " valid_phase=(\"Liq\", \"Vap\"), activity_coeff_model=\"Ideal\"\n",
- ")\n",
- "\n",
- "# Reaction package for the HDA reaction\n",
- "m.fs.reaction_params = reaction_props.HDAReactionParameterBlock(\n",
- " property_package=m.fs.BTHM_params\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Adding Unit Models\n",
- "\n",
- "Let us start adding the unit models we have imported to the flowsheet. Here, we are adding the Mixer (assigned a name M101) and a Heater (assigned a name H101). Note that, all unit models need to be given a property package argument. In addition, the Mixer unit model needs a `list` consisting of the inlets (toluene feed, hydrogen feed and vapor recycle streams in this case). "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 11,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Adding the mixer M101 to the flowsheet\n",
- "m.fs.M101 = Mixer(\n",
- " property_package=m.fs.BTHM_params,\n",
- " inlet_list=[\"toluene_feed\", \"hydrogen_feed\", \"vapor_recycle\"],\n",
- ")\n",
- "\n",
- "# Adding the heater H101 to the flowsheet\n",
- "m.fs.H101 = Heater(property_package=m.fs.BTHM_params, has_phase_equilibrium=True)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "
Inline Exercise:\n",
- "Let us now add the CSTR (assign the name R101) and pass the following arguments:\n",
- "
\n",
- " - \"property_package\": m.fs.BTHM_params
\n",
- " - \"reaction_package\": m.fs.reaction_params
\n",
- " - \"has_heat_of_reaction\": True
\n",
- " - \"has_heat_transfer\": True
\n",
- "
\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 13,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Add reactor with the specifications above\n",
- "m.fs.R101 = CSTR(\n",
- " property_package=m.fs.BTHM_params,\n",
- " reaction_package=m.fs.reaction_params,\n",
- " has_heat_of_reaction=True,\n",
- " has_heat_transfer=True,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Let us now add the Flash (assign the name F101), Splitter (assign the name S101) and PressureChanger (assign the name C101)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 14,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Adding the flash tank F101 to the flowsheet\n",
- "m.fs.F101 = Flash(\n",
- " property_package=m.fs.BTHM_params, has_heat_transfer=True, has_pressure_change=True\n",
- ")\n",
- "\n",
- "# Adding the splitter S101 to the flowsheet\n",
- "m.fs.S101 = Splitter(\n",
- " property_package=m.fs.BTHM_params, outlet_list=[\"purge\", \"recycle\"]\n",
- ")\n",
- "\n",
- "# Adding the compressor C101 to the flowsheet\n",
- "m.fs.C101 = PressureChanger(\n",
- " property_package=m.fs.BTHM_params,\n",
- " compressor=True,\n",
- " thermodynamic_assumption=ThermodynamicAssumption.isothermal,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Remark\n",
- "\n",
- "Currently, the `SequentialDecomposition()` tool, which we will later be using to initialize the flowsheet, does not support the distillation column model. Thus, we will first simulate the flowsheet without the distillation column. After it converges, we will then add the distillation column, initialize it, and simulate the entire flowsheet."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "As mentioned above, we use the `m.fs.BTHM_params` package, which contains all the four species, for the reactor loop, and the simpler `m.fs.BT_params` for unit operations following the flash (i.e., heater H102 and the distillation column D101). We define a `Translator` block to link the source property package and the package it is to be translated to in the following manner:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 15,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Add translator block to convert between property packages\n",
- "m.fs.translator = Translator(\n",
- " inlet_property_package=m.fs.BTHM_params, outlet_property_package=m.fs.BT_params\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Translator block constraints\n",
- "\n",
- "The `Translator` block needs to know how to translate between the two property packages. This must be custom coded for each application because of the generality of the IDAES framework.\n",
- "\n",
- "For this process, five constraints are required based on the state variables used in the outgoing process.\n",
- "\n",
- "- Since we assumed that only benzene and toluene are present in the liquid phase, the total molar flowrate must be the sum of molar flowrates of benzene and toluene, respectively.\n",
- "- Temperature of the inlet and outlet streams must be the same.\n",
- "- Pressure of the inlet and outgoing streams must be the same\n",
- "- The mole fraction of benzene in the outgoing stream is the ratio of the molar flowrate of liquid benzene in the inlet to the sum of molar flowrates of liquid benzene and toluene in the inlet.\n",
- "- The mole fraction of toluene in the outgoing stream is the ratio of the molar flowrate of liquid toluene in the inlet to the sum of molar flowrates of liquid benzene and toluene in the inlet."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 16,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Add constraint: Total flow = benzene flow + toluene flow (molar)\n",
- "m.fs.translator.eq_total_flow = Constraint(\n",
- " expr=m.fs.translator.outlet.flow_mol[0]\n",
- " == m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
- " + m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
- ")\n",
- "\n",
- "# Add constraint: Outlet temperature = Inlet temperature\n",
- "m.fs.translator.eq_temperature = Constraint(\n",
- " expr=m.fs.translator.outlet.temperature[0] == m.fs.translator.inlet.temperature[0]\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "In the above, note that the variable flow_mol_phase_comp has the index - [time, phase, component]. As this is a steady-state flowsheet, the time index by default is 0. The valid phases are [\"Liq\", \"Vap\"]. Similarly the valid component list is [\"benzene\", \"toluene\", \"hydrogen\", \"methane\"]."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "Add the constraint to ensure that the outlet pressure is the same as the inlet pressure\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 18,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Add constraint: Outlet pressure = Inlet pressure\n",
- "m.fs.translator.eq_pressure = Constraint(\n",
- " expr=m.fs.translator.outlet.pressure[0] == m.fs.translator.inlet.pressure[0]\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 19,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Remaining constraints on the translator block\n",
- "\n",
- "# Add constraint: Benzene mole fraction definition\n",
- "m.fs.translator.eq_mole_frac_benzene = Constraint(\n",
- " expr=m.fs.translator.outlet.mole_frac_comp[0, \"benzene\"]\n",
- " == m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
- " / (\n",
- " m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
- " + m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
- " )\n",
- ")\n",
- "\n",
- "# Add constraint: Toluene mole fraction definition\n",
- "m.fs.translator.eq_mole_frac_toluene = Constraint(\n",
- " expr=m.fs.translator.outlet.mole_frac_comp[0, \"toluene\"]\n",
- " == m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
- " / (\n",
- " m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
- " + m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
- " )\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "Finally, let us add the Heater H102 in the same way as H101 but pass the m.fs.BT_params thermodynamic package. We will add the distillation column after converging the flowsheet.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 21,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Add the Heater H102 to the flowsheet\n",
- "m.fs.H102 = Heater(\n",
- " property_package=m.fs.BT_params,\n",
- " has_pressure_change=True,\n",
- " has_phase_equilibrium=True,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Connecting Unit Models using Arcs\n",
- "\n",
- "We have now added the initial set of unit models to the flowsheet. However, we have not yet specifed how the units are connected. To do this, we will be using the `Arc` which is a pyomo component that takes in two arguments: `source` and `destination`. Let us connect the outlet of the mixer (M101) to the inlet of the heater (H101). "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 22,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.s03 = Arc(source=m.fs.M101.outlet, destination=m.fs.H101.inlet)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "![](HDA_flowsheet_distillation.png) \n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Now, connect the H101 outlet to the R101 inlet using the cell above as a guide. \n",
- "
\n",
- "\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 24,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Connect the H101 outlet to R101 inlet\n",
- "m.fs.s04 = Arc(source=m.fs.H101.outlet, destination=m.fs.R101.inlet)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We will now be connecting the rest of the units as shown below. Notice how the outlet names are different for the flash tank as it has a vapor and a liquid outlet. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 25,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.s05 = Arc(source=m.fs.R101.outlet, destination=m.fs.F101.inlet)\n",
- "m.fs.s06 = Arc(source=m.fs.F101.vap_outlet, destination=m.fs.S101.inlet)\n",
- "m.fs.s08 = Arc(source=m.fs.S101.recycle, destination=m.fs.C101.inlet)\n",
- "m.fs.s09 = Arc(source=m.fs.C101.outlet, destination=m.fs.M101.vapor_recycle)\n",
- "m.fs.s10a = Arc(source=m.fs.F101.liq_outlet, destination=m.fs.translator.inlet)\n",
- "m.fs.s10b = Arc(source=m.fs.translator.outlet, destination=m.fs.H102.inlet)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We have now connected the unit model block using the arcs. However, each of these arcs link to ports on the two unit models that are connected. In this case, the ports consist of the state variables that need to be linked between the unit models. Pyomo provides a convenient method to write these equality constraints for us between two ports and this is done as follows:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 26,
- "metadata": {},
- "outputs": [],
- "source": [
- "TransformationFactory(\"network.expand_arcs\").apply_to(m)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Appending additional constraints to the model\n",
- "\n",
- "Now, we will see how we can add additional constraints to the model using `Constraint` from Pyomo.\n",
- "\n",
- "Consider the reactor R101. By default, the conversion of a component is not calculated when we simulate the flowsheet. If we are interested either in specifying or constraining the conversion value, we can add the following constraint to calculate the conversion:\n",
- "$$ \\text{Conversion of toluene} = \\frac{\\text{molar flow of toluene in the inlet} - \\text{molar flow of toluene in the outlet}}{\\text{molar flow of toluene in the inlet}} $$ \n",
- "\n",
- "We add the constraint to the model as shown below."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 27,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Define the conversion variables using 'Var'\n",
- "m.fs.R101.conversion = Var(initialize=0.75, bounds=(0, 1))\n",
- "\n",
- "# Append the constraint to the model\n",
- "m.fs.R101.conv_constraint = Constraint(\n",
- " expr=m.fs.R101.conversion * m.fs.R101.inlet.flow_mol_phase_comp[0, \"Vap\", \"toluene\"]\n",
- " == (\n",
- " m.fs.R101.inlet.flow_mol_phase_comp[0, \"Vap\", \"toluene\"]\n",
- " - m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"toluene\"]\n",
- " )\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Fixing feed conditions and Initializing the flowsheet\n",
- "\n",
- "Let us first check how many degrees of freedom exist for this flowsheet using the `degrees_of_freedom` tool we imported earlier. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 28,
- "metadata": {},
- "outputs": [],
- "source": [
- "print(degrees_of_freedom(m))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 29,
- "metadata": {
- "tags": [
- "testing"
- ]
- },
- "outputs": [],
- "source": [
- "# Check the degrees of freedom\n",
- "assert degrees_of_freedom(m) == 29"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We will now be fixing the toluene feed stream to the conditions shown in the flowsheet above. Please note that though this is a pure toluene feed, the remaining components are still assigned a very small non-zero value to help with convergence and initializing. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 30,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"benzene\"].fix(1e-5)\n",
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"toluene\"].fix(1e-5)\n",
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"hydrogen\"].fix(1e-5)\n",
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"methane\"].fix(1e-5)\n",
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"benzene\"].fix(1e-5)\n",
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"toluene\"].fix(0.30)\n",
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"hydrogen\"].fix(1e-5)\n",
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"methane\"].fix(1e-5)\n",
- "m.fs.M101.toluene_feed.temperature.fix(303.2)\n",
- "m.fs.M101.toluene_feed.pressure.fix(350000)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Similarly, let us fix the hydrogen feed to the following conditions in the next cell:\n",
- " \n",
- " - FH2 = 0.30 mol/s
\n",
- " - FCH4 = 0.02 mol/s
\n",
- " - Remaining components = 1e-5 mol/s
\n",
- " - T = 303.2 K
\n",
- " - P = 350000 Pa
\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 31,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"benzene\"].fix(1e-5)\n",
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"toluene\"].fix(1e-5)\n",
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"hydrogen\"].fix(0.30)\n",
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"methane\"].fix(0.02)\n",
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"benzene\"].fix(1e-5)\n",
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"toluene\"].fix(1e-5)\n",
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"hydrogen\"].fix(1e-5)\n",
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"methane\"].fix(1e-5)\n",
- "m.fs.M101.hydrogen_feed.temperature.fix(303.2)\n",
- "m.fs.M101.hydrogen_feed.pressure.fix(350000)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Fixing unit model specifications\n",
- "\n",
- "Now that we have fixed our inlet feed conditions, we will now be fixing the operating conditions for the unit models in the flowsheet. Let us set the H101 outlet temperature to 600 K. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 32,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Fix the temperature of the outlet from the heater H101\n",
- "m.fs.H101.outlet.temperature.fix(600)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "
Inline Exercise:\n",
- "Set the conditions for the reactor R101 to the following conditions:\n",
- "
\n",
- " - `conversion` = 0.75
\n",
- " - `heat_duty` = 0
\n",
- "
\n",
- "\n",
- "Use Shift+Enter to run the cell once you have typed in your code. \n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 34,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Fix the 'conversion' of the reactor R101\n",
- "m.fs.R101.conversion.fix(0.75)\n",
- "\n",
- "# Todo: Fix the 'heat_duty' of the reactor R101\n",
- "m.fs.R101.heat_duty.fix(0)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The Flash conditions for F101 can be set as follows. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 35,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Fix the temperature of the vapor outlet from F101\n",
- "m.fs.F101.vap_outlet.temperature.fix(325.0)\n",
- "\n",
- "# Fix the pressure drop in the flash F101\n",
- "m.fs.F101.deltaP.fix(0)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Let us fix the split fraction of the purge stream from the splitter S101 and the outlet pressure from the compressor C101"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 36,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Fix the split fraction of the 'purge' stream from S101\n",
- "m.fs.S101.split_fraction[0, \"purge\"].fix(0.2)\n",
- "\n",
- "# Fix the pressure of the outlet from the compressor C101\n",
- "m.fs.C101.outlet.pressure.fix(350000)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Finally, let us fix the temperature of the outlet from H102 and the pressure drop in H102 as the following"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 37,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Fix the temperature of the outlet from the heater H102\n",
- "m.fs.H102.outlet.temperature.fix(375)\n",
- "\n",
- "# Fix the pressure drop in the heater H102\n",
- "m.fs.H102.deltaP.fix(-200000)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "To avoid convergence issues associated with poorly scaled variables and/or constraints, we scale the variables and constraints corresponding to the heaters H101 and H102, flash F101 and the reactor R101. Scaling factors for the flow rates, temperature, pressure, etc. have been defined in the property package: `ideal_VLE.py` file. Here, we set scaling factors only for the heat duty of the heater, the reaction extent, heat duty and volume of the reactor."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 38,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Set scaling factors for heat duty, reaction extent and volume\n",
- "iscale.set_scaling_factor(m.fs.H101.control_volume.heat, 1e-2)\n",
- "iscale.set_scaling_factor(m.fs.R101.control_volume.heat, 1e-2)\n",
- "iscale.set_scaling_factor(m.fs.R101.control_volume.rate_reaction_extent, 1)\n",
- "iscale.set_scaling_factor(m.fs.R101.control_volume.volume, 1)\n",
- "iscale.set_scaling_factor(m.fs.F101.control_volume.heat, 1e-2)\n",
- "iscale.set_scaling_factor(m.fs.H102.control_volume.heat, 1e-2)\n",
- "\n",
- "# Set the scaling factors for the remaining variables and all constraints\n",
- "iscale.calculate_scaling_factors(m.fs.H101)\n",
- "iscale.calculate_scaling_factors(m.fs.R101)\n",
- "iscale.calculate_scaling_factors(m.fs.F101)\n",
- "iscale.calculate_scaling_factors(m.fs.H102)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "We have now defined all the feed conditions and the inputs required for the unit models. The system should now have 0 degrees of freedom i.e. should be a square problem. Please check that the degrees of freedom is 0. \n",
- "\n",
- "Use Shift+Enter to run the cell once you have typed in your code. \n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 40,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Check the degrees of freedom\n",
- "print(degrees_of_freedom(m))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 41,
- "metadata": {
- "tags": [
- "testing"
- ]
- },
- "outputs": [],
- "source": [
- "# Check the degrees of freedom\n",
- "assert degrees_of_freedom(m) == 0"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Initialization\n",
- "\n",
- "This subsection will demonstrate how to use the built-in sequential decomposition tool to initialize our flowsheet.\n",
- "\n",
- "Let us first create an object for the `SequentialDecomposition` and specify our options for this. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 42,
- "metadata": {},
- "outputs": [],
- "source": [
- "seq = SequentialDecomposition()\n",
- "seq.options.select_tear_method = \"heuristic\"\n",
- "seq.options.tear_method = \"Wegstein\"\n",
- "seq.options.iterLim = 3\n",
- "\n",
- "# Using the SD tool\n",
- "G = seq.create_graph(m)\n",
- "heuristic_tear_set = seq.tear_set_arcs(G, method=\"heuristic\")\n",
- "order = seq.calculation_order(G)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Which is the tear stream? Display tear set and order"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 43,
- "metadata": {},
- "outputs": [],
- "source": [
- "for o in heuristic_tear_set:\n",
- " print(o.name)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "What sequence did the SD tool determine to solve this flowsheet with the least number of tears? "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 44,
- "metadata": {},
- "outputs": [],
- "source": [
- "for o in order:\n",
- " print(o[0].name)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The SequentialDecomposition tool has determined that the tear stream is the mixer outlet (s03 in the Figure above). We will need to provide a reasonable guess for this.\n",
- "\n",
- "For the initial guess, we assume that the flowrate of the recycle stream (s09) is zero. Consequently, the flow rate of the stream s03 is simply the sum of the flowrates of the toluene feed and hydrogen feed streams. Further, since the temperature and the pressure of both the toluene and hydrogen feed streams are the same, we specify their values as the initial guess for the temperature and pressure of the stream s03."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 45,
- "metadata": {},
- "outputs": [],
- "source": [
- "tear_guesses = {\n",
- " \"flow_mol_phase_comp\": {\n",
- " (0, \"Vap\", \"benzene\"): 1e-5,\n",
- " (0, \"Vap\", \"toluene\"): 1e-5,\n",
- " (0, \"Vap\", \"hydrogen\"): 0.30,\n",
- " (0, \"Vap\", \"methane\"): 0.02,\n",
- " (0, \"Liq\", \"benzene\"): 1e-5,\n",
- " (0, \"Liq\", \"toluene\"): 0.30,\n",
- " (0, \"Liq\", \"hydrogen\"): 1e-5,\n",
- " (0, \"Liq\", \"methane\"): 1e-5,\n",
- " },\n",
- " \"temperature\": {0: 303},\n",
- " \"pressure\": {0: 350000},\n",
- "}\n",
- "\n",
- "# Pass the tear_guess to the SD tool\n",
- "seq.set_guesses_for(m.fs.H101.inlet, tear_guesses)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Next, we need to tell the tool how to initialize a particular unit. We will be writing a python function which takes in a \"unit\" and calls the initialize method on that unit."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 46,
- "metadata": {},
- "outputs": [],
- "source": [
- "def function(unit):\n",
- " unit.initialize(outlvl=idaeslog.INFO)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We are now ready to initialize our flowsheet in a sequential mode. Note that we specifically set the iteration limit to be 3 as we are trying to use this tool only to get a good set of initial values such that IPOPT can then take over and solve this flowsheet for us. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 47,
- "metadata": {
- "scrolled": false
- },
- "outputs": [],
- "source": [
- "seq.run(m, function)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "We have now initialized the flowsheet. Let us run the flowsheet in a simulation mode to look at the results. \n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 48,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Create the solver object\n",
- "solver = get_solver()\n",
- "\n",
- "# Solve the model\n",
- "results = solver.solve(m, tee=True)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 49,
- "metadata": {
- "tags": [
- "testing"
- ]
- },
- "outputs": [],
- "source": [
- "# Check solver solve status\n",
- "from pyomo.environ import TerminationCondition\n",
- "\n",
- "assert results.solver.termination_condition == TerminationCondition.optimal"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Add distillation column \n",
- "\n",
- "As mentioned earlier, the `SequentialDecomposition` tool currently does not support the distillation column model. Thus, we have not included the distillation column in the flowsheet. Now that we have a converged flowsheet, we will add the distillation column and simulate the entire flowsheet. \n",
- "\n",
- "In the following, we will\n",
- "- Add the distillation column \n",
- "- Connect it to the heater \n",
- "- Add the necessary equality constraints\n",
- "- Propogate the state variable information from the outlet of the heater to the inlet of the distillation column \n",
- "- Fix the degrees of freedom of the distillation block (reflux ratio, boilup ratio, and condenser pressure)\n",
- "- Scale the control volume heat variables to help convergence\n",
- "- Initialize the distillation block.\n",
- "\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 50,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Add distillation column to the flowsheet\n",
- "m.fs.D101 = TrayColumn(\n",
- " number_of_trays=10,\n",
- " feed_tray_location=5,\n",
- " condenser_type=CondenserType.totalCondenser,\n",
- " condenser_temperature_spec=TemperatureSpec.atBubblePoint,\n",
- " property_package=m.fs.BT_params,\n",
- ")\n",
- "\n",
- "# Connect the outlet from the heater H102 to the distillation column\n",
- "m.fs.s11 = Arc(source=m.fs.H102.outlet, destination=m.fs.D101.feed)\n",
- "\n",
- "# Add the necessary equality constraints\n",
- "TransformationFactory(\"network.expand_arcs\").apply_to(m)\n",
- "\n",
- "# Propagate the state\n",
- "propagate_state(m.fs.s11)\n",
- "\n",
- "# Fix the reflux ratio, boilup ratio, and the condenser pressure\n",
- "m.fs.D101.condenser.reflux_ratio.fix(0.5)\n",
- "m.fs.D101.reboiler.boilup_ratio.fix(0.5)\n",
- "m.fs.D101.condenser.condenser_pressure.fix(150000)\n",
- "\n",
- "# set scaling factors\n",
- "# Set scaling factors for heat duty\n",
- "iscale.set_scaling_factor(m.fs.D101.condenser.control_volume.heat, 1e-2)\n",
- "iscale.set_scaling_factor(m.fs.D101.reboiler.control_volume.heat, 1e-2)\n",
- "\n",
- "# Set the scaling factors for the remaining variables and all constraints\n",
- "iscale.calculate_scaling_factors(m.fs.D101)\n",
- "\n",
- "# Initialize the distillation column\n",
- "m.fs.D101.initialize(outlvl=idaeslog.INFO)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Adding expressions to compute capital and operating costs\n",
- "\n",
- "In this section, we will add a few Expressions that allow us to evaluate the performance. Expressions provide a convenient way of calculating certain values that are a function of the variables defined in the model. For more details on Expressions, please refer to: https://pyomo.readthedocs.io/en/stable/pyomo_modeling_components/Expressions.html"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 51,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Expression to compute the total cooling cost\n",
- "m.fs.cooling_cost = Expression(\n",
- " expr=0.25e-7 * (-m.fs.F101.heat_duty[0])\n",
- " + 0.2e-7 * (-m.fs.D101.condenser.heat_duty[0])\n",
- ")\n",
- "\n",
- "# Expression to compute the total heating cost\n",
- "m.fs.heating_cost = Expression(\n",
- " expr=2.2e-7 * m.fs.H101.heat_duty[0]\n",
- " + 1.2e-7 * m.fs.H102.heat_duty[0]\n",
- " + 1.9e-7 * m.fs.D101.reboiler.heat_duty[0]\n",
- ")\n",
- "\n",
- "# Expression to compute the total operating cost\n",
- "m.fs.operating_cost = Expression(\n",
- " expr=(3600 * 24 * 365 * (m.fs.heating_cost + m.fs.cooling_cost))\n",
- ")\n",
- "\n",
- "# Expression to compute the total capital cost\n",
- "m.fs.capital_cost = Expression(expr=1e5 * m.fs.R101.volume[0])"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Solve the entire flowsheet"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 52,
- "metadata": {
- "tags": [
- "testing"
- ]
- },
- "outputs": [],
- "source": [
- "# Check that the degrees of freedom is zero\n",
- "assert degrees_of_freedom(m) == 0"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 53,
- "metadata": {},
- "outputs": [],
- "source": [
- "solver.solve(m, tee=True)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 54,
- "metadata": {
- "tags": [
- "testing"
- ]
- },
- "outputs": [],
- "source": [
- "# Check solver solve status\n",
- "from pyomo.environ import TerminationCondition\n",
- "\n",
- "assert results.solver.termination_condition == TerminationCondition.optimal"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Analyze the Results of the Square Problem\n",
- "\n",
- "How much is the total cost (operating cost + capital cost), operating cost, capital cost, benzene purity in the distillate from the distilation column, and conversion of toluene in the reactor?"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 55,
- "metadata": {},
- "outputs": [],
- "source": [
- "print(\"total cost = $\", value(m.fs.capital_cost) + value(m.fs.operating_cost))\n",
- "print(\"operating cost = $\", value(m.fs.operating_cost))\n",
- "print(\"capital cost = $\", value(m.fs.capital_cost))\n",
- "print()\n",
- "print(\n",
- " \"Distillate flowrate = \",\n",
- " value(m.fs.D101.condenser.distillate.flow_mol[0]()),\n",
- " \"mol/s\",\n",
- ")\n",
- "print(\n",
- " \"Benzene purity = \",\n",
- " 100 * value(m.fs.D101.condenser.distillate.mole_frac_comp[0, \"benzene\"]),\n",
- " \"%\",\n",
- ")\n",
- "print(\"Residue flowrate = \", value(m.fs.D101.reboiler.bottoms.flow_mol[0]()), \"mol/s\")\n",
- "print(\n",
- " \"Toluene purity = \",\n",
- " 100 * value(m.fs.D101.reboiler.bottoms.mole_frac_comp[0, \"toluene\"]),\n",
- " \"%\",\n",
- ")\n",
- "print()\n",
- "print(\"Conversion = \", 100 * value(m.fs.R101.conversion), \"%\")\n",
- "print()\n",
- "print(\n",
- " \"Overhead benzene loss in F101 = \",\n",
- " 100\n",
- " * value(m.fs.F101.vap_outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"])\n",
- " / value(m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]),\n",
- " \"%\",\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 56,
- "metadata": {
- "tags": [
- "testing"
- ]
- },
- "outputs": [],
- "source": [
- "import pytest\n",
- "\n",
- "print(value(m.fs.operating_cost))\n",
- "assert value(m.fs.operating_cost) == pytest.approx(427596.731, abs=100)\n",
- "print(value(m.fs.capital_cost))\n",
- "assert value(m.fs.capital_cost) == pytest.approx(14704.740, abs=100)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Get the state of the streams entering and leaving the reactor R101"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 57,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.R101.report()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Get the state of the streams entering and leaving the reactor R101"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 58,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.F101.report()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Next, let's look at how much benzene we are losing with the light gases out of F101. IDAES has tools for creating stream tables based on the `Arcs` and/or `Ports` in a flowsheet. Let us create and print a simple stream table showing the stream leaving the reactor and the vapor stream from F101.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "How much benzene are we losing in the F101 vapor outlet stream?\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 59,
- "metadata": {},
- "outputs": [],
- "source": [
- "from idaes.core.util.tables import (\n",
- " create_stream_table_dataframe,\n",
- " stream_table_dataframe_to_string,\n",
- ")\n",
- "\n",
- "st = create_stream_table_dataframe({\"Reactor\": m.fs.s05, \"Light Gases\": m.fs.s06})\n",
- "print(stream_table_dataframe_to_string(st))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "You can querry additional variables here if you like. \n",
- "\n",
- "Use Shift+Enter to run the cell once you have typed in your code. \n",
- "
"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Optimization\n",
- "\n",
- "\n",
- "We saw from the results above that the total operating cost for the base case was $442,297 per year. We are producing 0.162 mol/s of benzene at a purity of 89.5%. However, we are losing around 43.3% of benzene in F101 vapor outlet stream. \n",
- "\n",
- "Let us try to minimize this cost such that:\n",
- "- we are producing at least 0.18 mol/s of benzene as distillate i.e. our product stream\n",
- "- purity of benzene i.e. the mole fraction of benzene in the distillate is at least 99%\n",
- "- restricting the benzene loss in F101 vapor outlet to less than 20%\n",
- "\n",
- "For this problem, our decision variables are as follows:\n",
- "- H101 outlet temperature\n",
- "- R101 outlet temperature\n",
- "- F101 outlet temperature\n",
- "- H102 outlet temperature\n",
- "- Condenser pressure\n",
- "- reflux ratio\n",
- "- boilup ratio\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Let us declare our objective function for this problem. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 60,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.objective = Objective(expr=m.fs.operating_cost + m.fs.capital_cost)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Now, we need to unfix the decision variables as we had solved a square problem (degrees of freedom = 0) until now. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 61,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.H101.outlet.temperature.unfix()\n",
- "m.fs.R101.conversion.unfix()\n",
- "m.fs.F101.vap_outlet.temperature.unfix()\n",
- "m.fs.D101.condenser.condenser_pressure.unfix()\n",
- "m.fs.D101.condenser.reflux_ratio.unfix()\n",
- "m.fs.D101.reboiler.boilup_ratio.unfix()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "Let us now unfix the remaining variable: the temperature of the outlet from H102\n",
- "\n",
- "Use Shift+Enter to run the cell once you have typed in your code. \n",
- "
\n",
- "\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 63,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Unfix the temperature of the outlet from H102\n",
- "m.fs.H102.outlet.temperature.unfix()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Next, we need to set bounds on these decision variables to values shown below:\n",
- "\n",
- " - H101 outlet temperature [500, 600] K\n",
- " - R101 outlet temperature [600, 900] K\n",
- " - F101 outlet temperature [298, 450] K\n",
- " - H102 outlet temperature [350, 400] K\n",
- " - D101 condenser pressure [101325, 150000] Pa\n",
- " - D101 reflux ratio [0.1, 5]\n",
- " - D101 boilup ratio [0.1, 5]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 64,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Set bounds on the temperature of the outlet from H101\n",
- "m.fs.H101.outlet.temperature[0].setlb(500)\n",
- "m.fs.H101.outlet.temperature[0].setub(600)\n",
- "\n",
- "# Set bounds on the temperature of the outlet from R101\n",
- "m.fs.R101.outlet.temperature[0].setlb(600)\n",
- "m.fs.R101.outlet.temperature[0].setub(900)\n",
- "\n",
- "# Set bounds on the volume of the reactor R101\n",
- "m.fs.R101.volume[0].setlb(0)\n",
- "\n",
- "# Set bounds on the temperature of the vapor outlet from F101\n",
- "m.fs.F101.vap_outlet.temperature[0].setlb(298)\n",
- "m.fs.F101.vap_outlet.temperature[0].setub(450.0)\n",
- "\n",
- "# Set bounds on the temperature of the outlet from H102\n",
- "m.fs.H102.outlet.temperature[0].setlb(350)\n",
- "m.fs.H102.outlet.temperature[0].setub(400)\n",
- "\n",
- "# Set bounds on the pressure inside the condenser\n",
- "m.fs.D101.condenser.condenser_pressure.setlb(101325)\n",
- "m.fs.D101.condenser.condenser_pressure.setub(150000)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "Now, set the bounds for the D101 reflux ratio and boilup ratio.\n",
- "\n",
- "Use Shift+Enter to run the cell once you have typed in your code. \n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 66,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Set bounds on the reflux ratio\n",
- "m.fs.D101.condenser.reflux_ratio.setlb(0.1)\n",
- "m.fs.D101.condenser.reflux_ratio.setub(5)\n",
- "\n",
- "# Todo: Set bounds on the boilup ratio\n",
- "m.fs.D101.reboiler.boilup_ratio.setlb(0.1)\n",
- "m.fs.D101.reboiler.boilup_ratio.setub(5)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Now, the only things left to define are our constraints on overhead loss in F101, distillate flowrate and its purity. Let us first look at defining a constraint for the overhead loss in F101 where we are restricting the benzene leaving the vapor stream to less than 20 % of the benzene available in the reactor outlet. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 67,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Ensure that the overhead loss of benzene from F101 <= 20%\n",
- "m.fs.overhead_loss = Constraint(\n",
- " expr=m.fs.F101.vap_outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]\n",
- " <= 0.20 * m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "Now, add the constraint such that we are producing at least 0.18 mol/s of benzene in the product stream which is the distillate of D101. Let us name this constraint as m.fs.product_flow. \n",
- "\n",
- "Use Shift+Enter to run the cell once you have typed in your code. \n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 69,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Add minimum product flow constraint\n",
- "m.fs.product_flow = Constraint(expr=m.fs.D101.condenser.distillate.flow_mol[0] >= 0.18)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Let us add the final constraint on product purity or the mole fraction of benzene in the distillate such that it is at least greater than 99%. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 70,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.product_purity = Constraint(\n",
- " expr=m.fs.D101.condenser.distillate.mole_frac_comp[0, \"benzene\"] >= 0.99\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "We have now defined the optimization problem and we are now ready to solve this problem. \n",
- "\n",
- "\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 71,
- "metadata": {},
- "outputs": [],
- "source": [
- "results = solver.solve(m, tee=True)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 72,
- "metadata": {
- "tags": [
- "testing"
- ]
- },
- "outputs": [],
- "source": [
- "# Check solver solve status\n",
- "from pyomo.environ import TerminationCondition\n",
- "\n",
- "assert results.solver.termination_condition == TerminationCondition.optimal"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Optimization Results\n",
- "\n",
- "Display the results and product specifications"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 73,
- "metadata": {},
- "outputs": [],
- "source": [
- "print(\"total cost = $\", value(m.fs.capital_cost) + value(m.fs.operating_cost))\n",
- "print(\"operating cost = $\", value(m.fs.operating_cost))\n",
- "print(\"capital cost = $\", value(m.fs.capital_cost))\n",
- "print()\n",
- "print(\n",
- " \"Distillate flowrate = \",\n",
- " value(m.fs.D101.condenser.distillate.flow_mol[0]()),\n",
- " \"mol/s\",\n",
- ")\n",
- "print(\n",
- " \"Benzene purity = \",\n",
- " 100 * value(m.fs.D101.condenser.distillate.mole_frac_comp[0, \"benzene\"]),\n",
- " \"%\",\n",
- ")\n",
- "print(\"Residue flowrate = \", value(m.fs.D101.reboiler.bottoms.flow_mol[0]()), \"mol/s\")\n",
- "print(\n",
- " \"Toluene purity = \",\n",
- " 100 * value(m.fs.D101.reboiler.bottoms.mole_frac_comp[0, \"toluene\"]),\n",
- " \"%\",\n",
- ")\n",
- "print()\n",
- "print(\"Conversion = \", 100 * value(m.fs.R101.conversion), \"%\")\n",
- "print()\n",
- "print(\n",
- " \"Overhead benzene loss in F101 = \",\n",
- " 100\n",
- " * value(m.fs.F101.vap_outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"])\n",
- " / value(m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]),\n",
- " \"%\",\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 74,
- "metadata": {
- "tags": [
- "testing"
- ]
- },
- "outputs": [],
- "source": [
- "import pytest\n",
- "\n",
- "print(value(m.fs.operating_cost))\n",
- "print(value(m.fs.capital_cost))\n",
- "\n",
- "assert value(m.fs.operating_cost) == pytest.approx(408883.531, abs=100)\n",
- "assert value(m.fs.capital_cost) == pytest.approx(29956.367, abs=100)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Display optimal values for the decision variables"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 75,
- "metadata": {},
- "outputs": [],
- "source": [
- "print(\"Optimal Values\")\n",
- "print()\n",
- "\n",
- "print(\"H101 outlet temperature = \", value(m.fs.H101.outlet.temperature[0]), \"K\")\n",
- "\n",
- "print()\n",
- "print(\"R101 outlet temperature = \", value(m.fs.R101.outlet.temperature[0]), \"K\")\n",
- "\n",
- "print()\n",
- "print(\"F101 outlet temperature = \", value(m.fs.F101.vap_outlet.temperature[0]), \"K\")\n",
- "\n",
- "print()\n",
- "print(\"H102 outlet temperature = \", value(m.fs.H102.outlet.temperature[0]), \"K\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Key Takeaways\n",
- "\n",
- "Observe that the optimization was able to reduce the yearly operating cost from \\\\$427,593 to \\\\$408,342 (~4.5%). However, the amortized capital cost more than doubled from \\\\$14,704 to \\\\$29,927 due to the need to increase the conversion in the reactor (from 75% to 93%) to meet the production and purity constraints. \n",
- "\n",
- "Further, observe that the product flow rate and product purity are at their minimum values (0.18 mol/s and 99%, respectively). This is expected as increasing recovery would require more energy and cost to purify the product.\n",
- "\n",
- "\n",
- "Finally, observe that the operating temperature of the flash (F101) is almost at its lower bound. This helps in minimizing the amount of benzene in the vapor stream leaving the flash."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
+ "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": [
+ "\n",
+ "# HDA Flowsheet Simulation and Optimization\n",
+ "Maintainer: Brandon Paul \n",
+ "Author: Brandon Paul \n",
+ "Updated: 2023-06-01 \n",
+ "\n",
+ "\n",
+ "## Note\n",
+ "\n",
+ "This tutorial will be similar to the HDA flowsheet tutorial in the Tutorials section, except that we use a distillation column instead of a second flash (F102) to produce benzene and toluene products.\n",
+ "\n",
+ "\n",
+ "## Learning outcomes\n",
+ "\n",
+ "\n",
+ "- Construct a steady-state flowsheet using the IDAES unit model library\n",
+ "- Connecting unit models in a flowsheet using Arcs\n",
+ "- Using the SequentialDecomposition tool to initialize a flowsheet with recycle\n",
+ "- Fomulate and solve an optimization problem\n",
+ " - Defining an objective function\n",
+ " - Setting variable bounds\n",
+ " - Adding additional constraints \n",
+ "\n",
+ "\n",
+ "## Problem Statement\n",
+ "\n",
+ "Hydrodealkylation is a chemical reaction that often involves reacting\n",
+ "an aromatic hydrocarbon in the presence of hydrogen gas to form a\n",
+ "simpler aromatic hydrocarbon devoid of functional groups. In this\n",
+ "example, toluene will be reacted with hydrogen gas at high temperatures\n",
+ " to form benzene via the following reaction:\n",
+ "\n",
+ "**C6H5CH3 + H2 → C6H6 + CH4**\n",
+ "\n",
+ "\n",
+ "This reaction is often accompanied by an equilibrium side reaction\n",
+ "which forms diphenyl, which we will neglect for this example.\n",
+ "\n",
+ "This example is based on the 1967 AIChE Student Contest problem as\n",
+ "present by Douglas, J.M., Chemical Design of Chemical Processes, 1988,\n",
+ "McGraw-Hill.\n",
+ "\n",
+ "The flowsheet that we will be using for this module is shown below with the stream conditions. We will be processing toluene and hydrogen to produce at least 370 TPY of benzene. As shown in the flowsheet, we use a flash tank, F101, to separate out the non-condensibles, and a distillation column, D101, to further separate the benzene-toluene mixture to improve the benzene purity. The non-condensibles separated out in F101 will be partially recycled back to M101 and the rest will be purged. We will assume ideal gas behavior for this flowsheet. The properties required for this module are defined in\n",
+ "\n",
+ "- `hda_ideal_VLE.py`\n",
+ "- `idaes.models.properties.activity_coeff_models.BTX_activity_coeff_VLE`\n",
+ "- `hda_reaction.py`\n",
+ "\n",
+ "We will be using two thermodynamic packages: one (first in the list above) containing all four components (i.e., toluene, hydrogen, benzene, and methane) and the other (second in the list above) containing benzene and toluene only. The latter is needed to simplify the VLE calculations in the distillation column model. \n",
+ "\n",
+ "![](HDA_flowsheet_distillation.png)\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Translator block\n",
+ "\n",
+ "Benzene and toluene are separated by distillation, so the process involves phase equilibrium and two-phase flow conditions. However, the presence of hydrogen and methane complicates the calculations. This is because, hydrogen and methane are non-condensable under all conditions of interest; ergo, a vapor phase will always be present, and the mixture bubble point is extremely low. To simplify the phase equilibrium calculations, hydrogen and methane will be considered completely as non-condensable and insoluble in the liquid outlet from the flash F101.\n",
+ "\n",
+ "Since no hydrogen and methane will be present in the unit operations following the flash, a different component list can be used to simplify the property calculations. IDAES supports the definition of multiple property packages within a single flowsheet via `Translator` blocks. `Translator` blocks convert between different property calculations, component lists, and equations of state. "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Importing required pyomo and idaes components\n",
+ "\n",
+ "\n",
+ "To construct a flowsheet, we will need several components from the pyomo and idaes package. Let us first import the following components from Pyomo:\n",
+ "- Constraint (to write constraints)\n",
+ "- Var (to declare variables)\n",
+ "- ConcreteModel (to create the concrete model object)\n",
+ "- Expression (to evaluate values as a function of variables defined in the model)\n",
+ "- Objective (to define an objective function for optimization)\n",
+ "- SolverFactory (to solve the problem)\n",
+ "- TransformationFactory (to apply certain transformations)\n",
+ "- Arc (to connect two unit models)\n",
+ "- SequentialDecomposition (to initialize the flowsheet in a sequential mode)\n",
+ "\n",
+ "For further details on these components, please refer to the pyomo documentation: https://pyomo.readthedocs.io/en/stable/\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from pyomo.environ import (\n",
+ " Constraint,\n",
+ " Var,\n",
+ " ConcreteModel,\n",
+ " Expression,\n",
+ " Objective,\n",
+ " TransformationFactory,\n",
+ " value,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "Import `Arc` and `SequentialDecomposition` tools from `pyomo.network`\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Import the above mentioned tools from pyomo.network\n",
+ "from pyomo.network import Arc, SequentialDecomposition"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "From IDAES, we will be needing the FlowsheetBlock and the following unit models:\n",
+ "- Mixer\n",
+ "- Heater\n",
+ "- CSTR\n",
+ "- Flash\n",
+ "- Separator (splitter) \n",
+ "- PressureChanger\n",
+ "- Translator (to switch from one property package to another)\n",
+ "- TrayColumn (distillation column)\n",
+ "- CondenserType (Type of the overhead condenser: complete or partial)\n",
+ "- TemperatureSpec (Temperature specification inside the condenser)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from idaes.core import FlowsheetBlock"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from idaes.models.unit_models import (\n",
+ " PressureChanger,\n",
+ " Mixer,\n",
+ " Separator as Splitter,\n",
+ " Heater,\n",
+ " CSTR,\n",
+ " Flash,\n",
+ " Translator,\n",
+ ")\n",
+ "\n",
+ "from idaes.models_extra.column_models import TrayColumn\n",
+ "from idaes.models_extra.column_models.condenser import CondenserType, TemperatureSpec"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We will also be needing some utility tools to put together the flowsheet and calculate the degrees of freedom. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Utility tools to put together the flowsheet and calculate the degrees of freedom\n",
+ "from idaes.models.unit_models.pressure_changer import ThermodynamicAssumption\n",
+ "from idaes.core.util.model_statistics import degrees_of_freedom\n",
+ "from idaes.core.util.initialization import propagate_state\n",
+ "from idaes.core.solvers import get_solver\n",
+ "import idaes.core.util.scaling as iscale\n",
+ "from idaes.core.util.exceptions import InitializationError\n",
+ "\n",
+ "# Import idaes logger to set output levels\n",
+ "import idaes.logger as idaeslog"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Importing required thermo and reaction packages\n",
+ "\n",
+ "Finally, we import the thermophysical (`ideal_VLE.py` and `BTXParameterBlock`) packages and reaction package (`reaction.py`) for the HDA process. We have created custom thermophysical packages that assume ideal gas behavior with support for VLE. The reaction package consists of the stochiometric coefficients for the reaction, heat of reaction, and kinetic information (Arrhenius constant and activation energy). "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from idaes_examples.mod.hda import hda_reaction as reaction_props\n",
+ "from idaes.models.properties.activity_coeff_models.BTX_activity_coeff_VLE import (\n",
+ " BTXParameterBlock,\n",
+ ")\n",
+ "\n",
+ "from idaes_examples.mod.hda.hda_ideal_VLE import HDAParameterBlock"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Constructing the Flowsheet\n",
+ "\n",
+ "We have now imported all the components, unit models, and property modules we need to construct a flowsheet. Let us create a ConcreteModel and add the flowsheet block to it. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Create a Pyomo Concrete Model to contain the problem\n",
+ "m = ConcreteModel()\n",
+ "\n",
+ "# Add a steady state flowsheet block to the model\n",
+ "m.fs = FlowsheetBlock(dynamic=False)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We will now add the thermophysical and reaction packages to the flowsheet."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Property package for benzene, toluene, hydrogen, methane mixture\n",
+ "m.fs.BTHM_params = HDAParameterBlock()\n",
+ "\n",
+ "# Property package for the benzene-toluene mixture\n",
+ "m.fs.BT_params = BTXParameterBlock(\n",
+ " valid_phase=(\"Liq\", \"Vap\"), activity_coeff_model=\"Ideal\"\n",
+ ")\n",
+ "\n",
+ "# Reaction package for the HDA reaction\n",
+ "m.fs.reaction_params = reaction_props.HDAReactionParameterBlock(\n",
+ " property_package=m.fs.BTHM_params\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Adding Unit Models\n",
+ "\n",
+ "Let us start adding the unit models we have imported to the flowsheet. Here, we are adding the Mixer (assigned a name M101) and a Heater (assigned a name H101). Note that, all unit models need to be given a property package argument. In addition, the Mixer unit model needs a `list` consisting of the inlets (toluene feed, hydrogen feed and vapor recycle streams in this case). "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Adding the mixer M101 to the flowsheet\n",
+ "m.fs.M101 = Mixer(\n",
+ " property_package=m.fs.BTHM_params,\n",
+ " inlet_list=[\"toluene_feed\", \"hydrogen_feed\", \"vapor_recycle\"],\n",
+ ")\n",
+ "\n",
+ "# Adding the heater H101 to the flowsheet\n",
+ "m.fs.H101 = Heater(property_package=m.fs.BTHM_params, has_phase_equilibrium=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "
Inline Exercise:\n",
+ "Let us now add the CSTR (assign the name R101) and pass the following arguments:\n",
+ "
\n",
+ " - \"property_package\": m.fs.BTHM_params
\n",
+ " - \"reaction_package\": m.fs.reaction_params
\n",
+ " - \"has_heat_of_reaction\": True
\n",
+ " - \"has_heat_transfer\": True
\n",
+ "
\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Add reactor with the specifications above\n",
+ "m.fs.R101 = CSTR(\n",
+ " property_package=m.fs.BTHM_params,\n",
+ " reaction_package=m.fs.reaction_params,\n",
+ " has_heat_of_reaction=True,\n",
+ " has_heat_transfer=True,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let us now add the Flash (assign the name F101), Splitter (assign the name S101) and PressureChanger (assign the name C101)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Adding the flash tank F101 to the flowsheet\n",
+ "m.fs.F101 = Flash(\n",
+ " property_package=m.fs.BTHM_params, has_heat_transfer=True, has_pressure_change=True\n",
+ ")\n",
+ "\n",
+ "# Adding the splitter S101 to the flowsheet\n",
+ "m.fs.S101 = Splitter(\n",
+ " property_package=m.fs.BTHM_params, outlet_list=[\"purge\", \"recycle\"]\n",
+ ")\n",
+ "\n",
+ "# Adding the compressor C101 to the flowsheet\n",
+ "m.fs.C101 = PressureChanger(\n",
+ " property_package=m.fs.BTHM_params,\n",
+ " compressor=True,\n",
+ " thermodynamic_assumption=ThermodynamicAssumption.isothermal,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Remark\n",
+ "\n",
+ "Currently, the `SequentialDecomposition()` tool, which we will later be using to initialize the flowsheet, does not support the distillation column model. Thus, we will first simulate the flowsheet without the distillation column. After it converges, we will then add the distillation column, initialize it, and simulate the entire flowsheet."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As mentioned above, we use the `m.fs.BTHM_params` package, which contains all the four species, for the reactor loop, and the simpler `m.fs.BT_params` for unit operations following the flash (i.e., heater H102 and the distillation column D101). We define a `Translator` block to link the source property package and the package it is to be translated to in the following manner:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Add translator block to convert between property packages\n",
+ "m.fs.translator = Translator(\n",
+ " inlet_property_package=m.fs.BTHM_params, outlet_property_package=m.fs.BT_params\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Translator block constraints\n",
+ "\n",
+ "The `Translator` block needs to know how to translate between the two property packages. This must be custom coded for each application because of the generality of the IDAES framework.\n",
+ "\n",
+ "For this process, five constraints are required based on the state variables used in the outgoing process.\n",
+ "\n",
+ "- Since we assumed that only benzene and toluene are present in the liquid phase, the total molar flowrate must be the sum of molar flowrates of benzene and toluene, respectively.\n",
+ "- Temperature of the inlet and outlet streams must be the same.\n",
+ "- Pressure of the inlet and outgoing streams must be the same\n",
+ "- The mole fraction of benzene in the outgoing stream is the ratio of the molar flowrate of liquid benzene in the inlet to the sum of molar flowrates of liquid benzene and toluene in the inlet.\n",
+ "- The mole fraction of toluene in the outgoing stream is the ratio of the molar flowrate of liquid toluene in the inlet to the sum of molar flowrates of liquid benzene and toluene in the inlet."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Add constraint: Total flow = benzene flow + toluene flow (molar)\n",
+ "m.fs.translator.eq_total_flow = Constraint(\n",
+ " expr=m.fs.translator.outlet.flow_mol[0]\n",
+ " == m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
+ " + m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
+ ")\n",
+ "\n",
+ "# Add constraint: Outlet temperature = Inlet temperature\n",
+ "m.fs.translator.eq_temperature = Constraint(\n",
+ " expr=m.fs.translator.outlet.temperature[0] == m.fs.translator.inlet.temperature[0]\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In the above, note that the variable flow_mol_phase_comp has the index - [time, phase, component]. As this is a steady-state flowsheet, the time index by default is 0. The valid phases are [\"Liq\", \"Vap\"]. Similarly the valid component list is [\"benzene\", \"toluene\", \"hydrogen\", \"methane\"]."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "Add the constraint to ensure that the outlet pressure is the same as the inlet pressure\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Add constraint: Outlet pressure = Inlet pressure\n",
+ "m.fs.translator.eq_pressure = Constraint(\n",
+ " expr=m.fs.translator.outlet.pressure[0] == m.fs.translator.inlet.pressure[0]\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Remaining constraints on the translator block\n",
+ "\n",
+ "# Add constraint: Benzene mole fraction definition\n",
+ "m.fs.translator.eq_mole_frac_benzene = Constraint(\n",
+ " expr=m.fs.translator.outlet.mole_frac_comp[0, \"benzene\"]\n",
+ " == m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
+ " / (\n",
+ " m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
+ " + m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
+ " )\n",
+ ")\n",
+ "\n",
+ "# Add constraint: Toluene mole fraction definition\n",
+ "m.fs.translator.eq_mole_frac_toluene = Constraint(\n",
+ " expr=m.fs.translator.outlet.mole_frac_comp[0, \"toluene\"]\n",
+ " == m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
+ " / (\n",
+ " m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
+ " + m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
+ " )\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "Finally, let us add the Heater H102 in the same way as H101 but pass the m.fs.BT_params thermodynamic package. We will add the distillation column after converging the flowsheet.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Add the Heater H102 to the flowsheet\n",
+ "m.fs.H102 = Heater(\n",
+ " property_package=m.fs.BT_params,\n",
+ " has_pressure_change=True,\n",
+ " has_phase_equilibrium=True,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Connecting Unit Models using Arcs\n",
+ "\n",
+ "We have now added the initial set of unit models to the flowsheet. However, we have not yet specified how the units are connected. To do this, we will be using the `Arc` which is a pyomo component that takes in two arguments: `source` and `destination`. Let us connect the outlet of the mixer (M101) to the inlet of the heater (H101). "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.fs.s03 = Arc(source=m.fs.M101.outlet, destination=m.fs.H101.inlet)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "![](HDA_flowsheet_distillation.png) \n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Now, connect the H101 outlet to the R101 inlet using the cell above as a guide. \n",
+ "
\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Connect the H101 outlet to R101 inlet\n",
+ "m.fs.s04 = Arc(source=m.fs.H101.outlet, destination=m.fs.R101.inlet)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We will now be connecting the rest of the units as shown below. Notice how the outlet names are different for the flash tank as it has a vapor and a liquid outlet. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.fs.s05 = Arc(source=m.fs.R101.outlet, destination=m.fs.F101.inlet)\n",
+ "m.fs.s06 = Arc(source=m.fs.F101.vap_outlet, destination=m.fs.S101.inlet)\n",
+ "m.fs.s08 = Arc(source=m.fs.S101.recycle, destination=m.fs.C101.inlet)\n",
+ "m.fs.s09 = Arc(source=m.fs.C101.outlet, destination=m.fs.M101.vapor_recycle)\n",
+ "m.fs.s10a = Arc(source=m.fs.F101.liq_outlet, destination=m.fs.translator.inlet)\n",
+ "m.fs.s10b = Arc(source=m.fs.translator.outlet, destination=m.fs.H102.inlet)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We have now connected the unit model block using the arcs. However, each of these arcs link to ports on the two unit models that are connected. In this case, the ports consist of the state variables that need to be linked between the unit models. Pyomo provides a convenient method to write these equality constraints for us between two ports and this is done as follows:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 26,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "TransformationFactory(\"network.expand_arcs\").apply_to(m)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Appending additional constraints to the model\n",
+ "\n",
+ "Now, we will see how we can add additional constraints to the model using `Constraint` from Pyomo.\n",
+ "\n",
+ "Consider the reactor R101. By default, the conversion of a component is not calculated when we simulate the flowsheet. If we are interested either in specifying or constraining the conversion value, we can add the following constraint to calculate the conversion:\n",
+ "$$ \\text{Conversion of toluene} = \\frac{\\text{molar flow of toluene in the inlet} - \\text{molar flow of toluene in the outlet}}{\\text{molar flow of toluene in the inlet}} $$ \n",
+ "\n",
+ "We add the constraint to the model as shown below."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 27,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Define the conversion variables using 'Var'\n",
+ "m.fs.R101.conversion = Var(initialize=0.75, bounds=(0, 1))\n",
+ "\n",
+ "# Append the constraint to the model\n",
+ "m.fs.R101.conv_constraint = Constraint(\n",
+ " expr=m.fs.R101.conversion * m.fs.R101.inlet.flow_mol_phase_comp[0, \"Vap\", \"toluene\"]\n",
+ " == (\n",
+ " m.fs.R101.inlet.flow_mol_phase_comp[0, \"Vap\", \"toluene\"]\n",
+ " - m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"toluene\"]\n",
+ " )\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Fixing feed conditions and Initializing the flowsheet\n",
+ "\n",
+ "Let us first check how many degrees of freedom exist for this flowsheet using the `degrees_of_freedom` tool we imported earlier. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 28,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "29\n"
+ ]
}
- ],
- "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.8.16"
+ ],
+ "source": [
+ "print(degrees_of_freedom(m))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 29,
+ "metadata": {
+ "tags": [
+ "testing"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Check the degrees of freedom\n",
+ "assert degrees_of_freedom(m) == 29"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We will now be fixing the toluene feed stream to the conditions shown in the flowsheet above. Please note that though this is a pure toluene feed, the remaining components are still assigned a very small non-zero value to help with convergence and initializing. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 30,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"benzene\"].fix(1e-5)\n",
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"toluene\"].fix(1e-5)\n",
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"hydrogen\"].fix(1e-5)\n",
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"methane\"].fix(1e-5)\n",
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"benzene\"].fix(1e-5)\n",
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"toluene\"].fix(0.30)\n",
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"hydrogen\"].fix(1e-5)\n",
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"methane\"].fix(1e-5)\n",
+ "m.fs.M101.toluene_feed.temperature.fix(303.2)\n",
+ "m.fs.M101.toluene_feed.pressure.fix(350000)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Similarly, let us fix the hydrogen feed to the following conditions in the next cell:\n",
+ " \n",
+ " - FH2 = 0.30 mol/s
\n",
+ " - FCH4 = 0.02 mol/s
\n",
+ " - Remaining components = 1e-5 mol/s
\n",
+ " - T = 303.2 K
\n",
+ " - P = 350000 Pa
\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 31,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"benzene\"].fix(1e-5)\n",
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"toluene\"].fix(1e-5)\n",
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"hydrogen\"].fix(0.30)\n",
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"methane\"].fix(0.02)\n",
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"benzene\"].fix(1e-5)\n",
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"toluene\"].fix(1e-5)\n",
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"hydrogen\"].fix(1e-5)\n",
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"methane\"].fix(1e-5)\n",
+ "m.fs.M101.hydrogen_feed.temperature.fix(303.2)\n",
+ "m.fs.M101.hydrogen_feed.pressure.fix(350000)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Fixing unit model specifications\n",
+ "\n",
+ "Now that we have fixed our inlet feed conditions, we will now be fixing the operating conditions for the unit models in the flowsheet. Let us set the H101 outlet temperature to 600 K. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 32,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Fix the temperature of the outlet from the heater H101\n",
+ "m.fs.H101.outlet.temperature.fix(600)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "
Inline Exercise:\n",
+ "Set the conditions for the reactor R101 to the following conditions:\n",
+ "
\n",
+ " - `conversion` = 0.75
\n",
+ " - `heat_duty` = 0
\n",
+ "
\n",
+ "\n",
+ "Use Shift+Enter to run the cell once you have typed in your code. \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 34,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Fix the 'conversion' of the reactor R101\n",
+ "m.fs.R101.conversion.fix(0.75)\n",
+ "\n",
+ "# Todo: Fix the 'heat_duty' of the reactor R101\n",
+ "m.fs.R101.heat_duty.fix(0)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The Flash conditions for F101 can be set as follows. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 35,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Fix the temperature of the vapor outlet from F101\n",
+ "m.fs.F101.vap_outlet.temperature.fix(325.0)\n",
+ "\n",
+ "# Fix the pressure drop in the flash F101\n",
+ "m.fs.F101.deltaP.fix(0)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let us fix the split fraction of the purge stream from the splitter S101 and the outlet pressure from the compressor C101"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 36,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Fix the split fraction of the 'purge' stream from S101\n",
+ "m.fs.S101.split_fraction[0, \"purge\"].fix(0.2)\n",
+ "\n",
+ "# Fix the pressure of the outlet from the compressor C101\n",
+ "m.fs.C101.outlet.pressure.fix(350000)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Finally, let us fix the temperature of the outlet from H102 and the pressure drop in H102 as the following"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 37,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Fix the temperature of the outlet from the heater H102\n",
+ "m.fs.H102.outlet.temperature.fix(375)\n",
+ "\n",
+ "# Fix the pressure drop in the heater H102\n",
+ "m.fs.H102.deltaP.fix(-200000)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "To avoid convergence issues associated with poorly scaled variables and/or constraints, we scale the variables and constraints corresponding to the heaters H101 and H102, flash F101 and the reactor R101. Scaling factors for the flow rates, temperature, pressure, etc. have been defined in the property package: `ideal_VLE.py` file. Here, we set scaling factors only for the heat duty of the heater, the reaction extent, heat duty and volume of the reactor."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 38,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enth_mol_phase_comp[Liq,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enth_mol_phase_comp[Liq,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enth_mol_phase_comp[Vap,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enth_mol_phase_comp[Vap,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].enth_mol_phase_comp[Liq,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].enth_mol_phase_comp[Liq,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].enth_mol_phase_comp[Vap,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].enth_mol_phase_comp[Vap,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].material_flow_terms[Liq,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].material_flow_terms[Vap,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].material_flow_terms[Liq,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].material_flow_terms[Vap,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enthalpy_flow_terms[Liq], enthalpy_flow_terms\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enthalpy_flow_terms[Vap], enthalpy_flow_terms\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Set scaling factors for heat duty, reaction extent and volume\n",
+ "iscale.set_scaling_factor(m.fs.H101.control_volume.heat, 1e-2)\n",
+ "iscale.set_scaling_factor(m.fs.R101.control_volume.heat, 1e-2)\n",
+ "iscale.set_scaling_factor(m.fs.R101.control_volume.rate_reaction_extent, 1)\n",
+ "iscale.set_scaling_factor(m.fs.R101.control_volume.volume, 1)\n",
+ "iscale.set_scaling_factor(m.fs.F101.control_volume.heat, 1e-2)\n",
+ "iscale.set_scaling_factor(m.fs.H102.control_volume.heat, 1e-2)\n",
+ "\n",
+ "# Set the scaling factors for the remaining variables and all constraints\n",
+ "iscale.calculate_scaling_factors(m.fs.H101)\n",
+ "iscale.calculate_scaling_factors(m.fs.R101)\n",
+ "iscale.calculate_scaling_factors(m.fs.F101)\n",
+ "iscale.calculate_scaling_factors(m.fs.H102)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "We have now defined all the feed conditions and the inputs required for the unit models. The system should now have 0 degrees of freedom i.e. should be a square problem. Please check that the degrees of freedom is 0. \n",
+ "\n",
+ "Use Shift+Enter to run the cell once you have typed in your code. \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 40,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "0\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Todo: Check the degrees of freedom\n",
+ "print(degrees_of_freedom(m))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 41,
+ "metadata": {
+ "tags": [
+ "testing"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Check the degrees of freedom\n",
+ "assert degrees_of_freedom(m) == 0"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Initialization\n",
+ "\n",
+ "This subsection will demonstrate how to use the built-in sequential decomposition tool to initialize our flowsheet.\n",
+ "\n",
+ "Let us first create an object for the `SequentialDecomposition` and specify our options for this. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 42,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "seq = SequentialDecomposition()\n",
+ "seq.options.select_tear_method = \"heuristic\"\n",
+ "seq.options.tear_method = \"Wegstein\"\n",
+ "seq.options.iterLim = 3\n",
+ "\n",
+ "# Using the SD tool\n",
+ "G = seq.create_graph(m)\n",
+ "heuristic_tear_set = seq.tear_set_arcs(G, method=\"heuristic\")\n",
+ "order = seq.calculation_order(G)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Which is the tear stream? Display tear set and order"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 43,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "fs.s03\n"
+ ]
+ }
+ ],
+ "source": [
+ "for o in heuristic_tear_set:\n",
+ " print(o.name)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "What sequence did the SD tool determine to solve this flowsheet with the least number of tears? "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 44,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "fs.H101\n",
+ "fs.R101\n",
+ "fs.F101\n",
+ "fs.S101\n",
+ "fs.C101\n",
+ "fs.M101\n"
+ ]
+ }
+ ],
+ "source": [
+ "for o in order:\n",
+ " print(o[0].name)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The SequentialDecomposition tool has determined that the tear stream is the mixer outlet (s03 in the Figure above). We will need to provide a reasonable guess for this.\n",
+ "\n",
+ "For the initial guess, we assume that the flowrate of the recycle stream (s09) is zero. Consequently, the flow rate of the stream s03 is simply the sum of the flowrates of the toluene feed and hydrogen feed streams. Further, since the temperature and the pressure of both the toluene and hydrogen feed streams are the same, we specify their values as the initial guess for the temperature and pressure of the stream s03."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 45,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "tear_guesses = {\n",
+ " \"flow_mol_phase_comp\": {\n",
+ " (0, \"Vap\", \"benzene\"): 1e-5,\n",
+ " (0, \"Vap\", \"toluene\"): 1e-5,\n",
+ " (0, \"Vap\", \"hydrogen\"): 0.30,\n",
+ " (0, \"Vap\", \"methane\"): 0.02,\n",
+ " (0, \"Liq\", \"benzene\"): 1e-5,\n",
+ " (0, \"Liq\", \"toluene\"): 0.30,\n",
+ " (0, \"Liq\", \"hydrogen\"): 1e-5,\n",
+ " (0, \"Liq\", \"methane\"): 1e-5,\n",
+ " },\n",
+ " \"temperature\": {0: 303.2},\n",
+ " \"pressure\": {0: 350000},\n",
+ "}\n",
+ "\n",
+ "# Pass the tear_guess to the SD tool\n",
+ "seq.set_guesses_for(m.fs.H101.inlet, tear_guesses)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Next, we need to tell the tool how to initialize a particular unit. We will be writing a python function which takes in a \"unit\" and calls the initialize method on that unit. For the initialization, we will import a Block Triangularization Initializer which decomposes the model into a set of subproblems. These subproblems are solved using a block triangularization transformation before applying a simple Newton or user-selected solver. Methods such as block triangularization often solve faster and yield more reliable behavior than heuristic methods, but sometime struggle to decompose models with strongly coupled equations (e.g. column models, systems with counter-current flow, vapor-liquid equilibrium)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 46,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def function(unit):\n",
+ " # Try initializing using default initializer,\n",
+ " # if it fails (probably due to scaling) try for the second time\n",
+ " try:\n",
+ " initializer = unit.default_initializer()\n",
+ " initializer.initialize(unit, output_level=idaeslog.INFO)\n",
+ " except InitializationError:\n",
+ " solver = get_solver()\n",
+ " solver.solve(unit)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We are now ready to initialize our flowsheet in a sequential mode. Note that we specifically set the iteration limit to be 3 as we are trying to use this tool only to get a good set of initial values such that IPOPT can then take over and solve this flowsheet for us. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 47,
+ "metadata": {
+ "scrolled": false
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2024-08-28 18:38:14 [INFO] idaes.init.fs.H101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:16 [INFO] idaes.init.fs.H101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:16 [INFO] idaes.init.fs.R101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:16 [INFO] idaes.init.fs.R101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.F101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.F101.control_volume.properties_out: Initialization Complete\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 12\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 50 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.S101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - \n",
+ "2024-08-28 18:38:18 [INFO] idaes.init.fs.C101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:18 [INFO] idaes.init.fs.C101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:18 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 1 optimal - .\n",
+ "2024-08-28 18:38:18 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 2 optimal - .\n",
+ "2024-08-28 18:38:19 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 3 optimal - .\n",
+ "2024-08-28 18:38:19 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 4 optimal - .\n",
+ "2024-08-28 18:38:19 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 5 optimal - .\n",
+ "2024-08-28 18:38:19 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 1 optimal - .\n",
+ "2024-08-28 18:38:19 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 2 optimal - .\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 3 optimal - .\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 4 optimal - .\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 5 optimal - .\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.M101.toluene_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.M101.hydrogen_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.M101.vapor_recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:21 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:21 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - \n",
+ "2024-08-28 18:38:21 [INFO] idaes.init.fs.H101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:21 [INFO] idaes.init.fs.H101.control_volume.properties_out: Initialization Complete\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 11\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 50 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.R101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.R101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.F101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.F101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.S101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - \n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.C101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.C101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.M101.toluene_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.M101.hydrogen_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.M101.vapor_recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - \n",
+ "2024-08-28 18:38:24 [INFO] idaes.init.fs.H101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:24 [INFO] idaes.init.fs.H101.control_volume.properties_out: Initialization Complete\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 11\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 50 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "2024-08-28 18:38:24 [INFO] idaes.init.fs.R101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:24 [INFO] idaes.init.fs.R101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.F101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.F101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.S101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - \n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.C101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.C101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.M101.toluene_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.M101.hydrogen_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.M101.vapor_recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - \n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.H101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.H101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.R101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.R101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.F101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.F101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.S101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - \n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.C101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.C101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.M101.toluene_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.M101.hydrogen_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.M101.vapor_recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - \n",
+ "2024-08-28 18:38:29 [INFO] idaes.init.fs.H101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:29 [INFO] idaes.init.fs.H101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:29 [INFO] idaes.init.fs.R101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:29 [INFO] idaes.init.fs.R101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.F101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.F101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.S101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - \n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.C101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.C101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.M101.toluene_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.M101.hydrogen_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:31 [INFO] idaes.init.fs.M101.vapor_recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:31 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:31 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - \n",
+ "WARNING: Wegstein failed to converge in 3 iterations\n",
+ "2024-08-28 18:38:31 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 1 optimal - .\n",
+ "2024-08-28 18:38:31 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 2 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 3 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 4 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 5 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 1 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 2 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 3 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 4 optimal - .\n",
+ "2024-08-28 18:38:33 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 5 optimal - .\n"
+ ]
+ }
+ ],
+ "source": [
+ "seq.run(m, function)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "We have now initialized the flowsheet. Let us run the flowsheet in a simulation mode to look at the results. \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 48,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "WARNING: model contains export suffix 'scaling_factor' that contains 6\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 150 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "Ipopt 3.13.2: nlp_scaling_method=gradient-based\n",
+ "tol=1e-06\n",
+ "max_iter=200\n",
+ "\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...: 1097\n",
+ "Number of nonzeros in inequality constraint Jacobian.: 0\n",
+ "Number of nonzeros in Lagrangian Hessian.............: 877\n",
+ "\n",
+ "Total number of variables............................: 363\n",
+ " variables with only lower bounds: 8\n",
+ " variables with lower and upper bounds: 155\n",
+ " variables with only upper bounds: 0\n",
+ "Total number of equality constraints.................: 363\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.82e+04 1.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
+ " 1 0.0000000e+00 8.69e+03 1.44e+03 -1.0 2.00e+04 - 9.71e-01 4.67e-01H 1\n",
+ " 2 0.0000000e+00 1.29e+03 1.56e+03 -1.0 1.60e+04 - 9.79e-01 4.90e-01h 1\n",
+ " 3 0.0000000e+00 1.18e+03 1.55e+05 -1.0 1.40e+04 - 9.90e-01 4.99e-01h 1\n",
+ " 4 0.0000000e+00 5.46e+02 2.32e+09 -1.0 8.42e+03 - 1.00e+00 9.82e-01h 1\n",
+ " 5 0.0000000e+00 5.46e+03 3.66e+10 -1.0 5.97e+02 - 1.00e+00 9.90e-01h 1\n",
+ " 6 0.0000000e+00 1.21e+03 8.01e+09 -1.0 5.75e+00 - 1.00e+00 1.00e+00h 1\n",
+ " 7 0.0000000e+00 6.41e+00 3.87e+07 -1.0 1.53e-03 - 1.00e+00 1.00e+00f 1\n",
+ " 8 0.0000000e+00 1.96e-04 9.36e+02 -1.0 7.28e-06 - 1.00e+00 1.00e+00h 1\n",
+ " 9 0.0000000e+00 2.24e-08 4.99e-01 -3.8 5.92e-08 - 1.00e+00 1.00e+00h 1\n",
+ "Cannot recompute multipliers for feasibility problem. Error in eq_mult_calculator\n",
+ "\n",
+ "Number of Iterations....: 9\n",
+ "\n",
+ " (scaled) (unscaled)\n",
+ "Objective...............: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Dual infeasibility......: 1.5042487592972509e+04 1.5042487592972509e+04\n",
+ "Constraint violation....: 2.9103830456733704e-11 2.2351741790771484e-08\n",
+ "Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Overall NLP error.......: 2.9103830456733704e-11 1.5042487592972509e+04\n",
+ "\n",
+ "\n",
+ "Number of objective function evaluations = 11\n",
+ "Number of objective gradient evaluations = 10\n",
+ "Number of equality constraint evaluations = 11\n",
+ "Number of inequality constraint evaluations = 0\n",
+ "Number of equality constraint Jacobian evaluations = 10\n",
+ "Number of inequality constraint Jacobian evaluations = 0\n",
+ "Number of Lagrangian Hessian evaluations = 9\n",
+ "Total CPU secs in IPOPT (w/o function evaluations) = 0.011\n",
+ "Total CPU secs in NLP function evaluations = 0.001\n",
+ "\n",
+ "EXIT: Optimal Solution Found.\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Create the solver object\n",
+ "solver = get_solver()\n",
+ "\n",
+ "# Solve the model\n",
+ "results = solver.solve(m, tee=True)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 49,
+ "metadata": {
+ "tags": [
+ "testing"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Check solver solve status\n",
+ "from pyomo.environ import TerminationCondition\n",
+ "\n",
+ "assert results.solver.termination_condition == TerminationCondition.optimal"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Add distillation column \n",
+ "\n",
+ "As mentioned earlier, the `SequentialDecomposition` tool currently does not support the distillation column model. Thus, we have not included the distillation column in the flowsheet. Now that we have a converged flowsheet, we will add the distillation column and simulate the entire flowsheet. \n",
+ "\n",
+ "In the following, we will\n",
+ "- Add the distillation column \n",
+ "- Connect it to the heater \n",
+ "- Add the necessary equality constraints\n",
+ "- Propagate the state variable information from the outlet of the heater to the inlet of the distillation column \n",
+ "- Fix the degrees of freedom of the distillation block (reflux ratio, boilup ratio, and condenser pressure)\n",
+ "- Scale the control volume heat variables to help convergence\n",
+ "- Initialize the distillation block.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 50,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].material_flow_terms[Liq,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].material_flow_terms[Vap,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].material_flow_terms[Liq,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].material_flow_terms[Vap,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].enthalpy_flow_terms[Liq], enthalpy_flow_terms\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].enthalpy_flow_terms[Vap], enthalpy_flow_terms\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].material_flow_terms[Liq,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].material_flow_terms[Vap,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].material_flow_terms[Liq,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].material_flow_terms[Vap,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].enthalpy_flow_terms[Liq], enthalpy_flow_terms\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].enthalpy_flow_terms[Vap], enthalpy_flow_terms\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.e_flow_reflux[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.e_mole_frac_reflux[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.e_mole_frac_reflux[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.e_flow_vapor_reboil[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.e_mole_frac_vapor_reboil[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.e_mole_frac_vapor_reboil[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101: Begin initialization.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray: Begin initialization.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: State Released.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: State Released.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser.control_volume: Initialization Complete\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser: Initialization Complete, optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: State Released.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: State Released.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler: Initialization Complete, optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: State Released.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.rectification_section[1]: Begin initialization.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: State Released.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[2]: Begin initialization.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: State Released.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[2]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[2]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3]: Begin initialization.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: State Released.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[3]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[3]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[3]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4]: Begin initialization.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: State Released.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.stripping_section[6]: Begin initialization.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: State Released.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[7]: Begin initialization.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: State Released.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[7]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[7]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8]: Begin initialization.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: State Released.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[8]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[8]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[8]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9]: Begin initialization.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: State Released.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10]: Begin initialization.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: State Released.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: State Released.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101: Rectification section initialization status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101: Stripping section initialization status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: State Released.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: State Released.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101: Column section initialization status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: State Released.\n",
+ "2024-08-28 18:39:02 [INFO] idaes.init.fs.D101: Column section + Condenser initialization status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:02 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: State Released.\n",
+ "2024-08-28 18:39:03 [INFO] idaes.init.fs.D101: Column section + Condenser + Reboiler initialization status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:03 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: State Released.\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Add distillation column to the flowsheet\n",
+ "m.fs.D101 = TrayColumn(\n",
+ " number_of_trays=10,\n",
+ " feed_tray_location=5,\n",
+ " condenser_type=CondenserType.totalCondenser,\n",
+ " condenser_temperature_spec=TemperatureSpec.atBubblePoint,\n",
+ " property_package=m.fs.BT_params,\n",
+ ")\n",
+ "\n",
+ "# Connect the outlet from the heater H102 to the distillation column\n",
+ "m.fs.s11 = Arc(source=m.fs.H102.outlet, destination=m.fs.D101.feed)\n",
+ "\n",
+ "# Add the necessary equality constraints\n",
+ "TransformationFactory(\"network.expand_arcs\").apply_to(m)\n",
+ "\n",
+ "# Propagate the state\n",
+ "propagate_state(m.fs.s11)\n",
+ "\n",
+ "# Fix the reflux ratio, boilup ratio, and the condenser pressure\n",
+ "m.fs.D101.condenser.reflux_ratio.fix(0.5)\n",
+ "m.fs.D101.reboiler.boilup_ratio.fix(0.5)\n",
+ "m.fs.D101.condenser.condenser_pressure.fix(150000)\n",
+ "\n",
+ "# set scaling factors\n",
+ "# Set scaling factors for heat duty\n",
+ "iscale.set_scaling_factor(m.fs.D101.condenser.control_volume.heat, 1e-2)\n",
+ "iscale.set_scaling_factor(m.fs.D101.reboiler.control_volume.heat, 1e-2)\n",
+ "\n",
+ "# Set the scaling factors for the remaining variables and all constraints\n",
+ "iscale.calculate_scaling_factors(m.fs.D101)\n",
+ "\n",
+ "# Initialize the distillation column\n",
+ "m.fs.D101.initialize(outlvl=idaeslog.INFO)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Adding expressions to compute capital and operating costs\n",
+ "\n",
+ "In this section, we will add a few Expressions that allow us to evaluate the performance. Expressions provide a convenient way of calculating certain values that are a function of the variables defined in the model. For more details on Expressions, please refer to: https://pyomo.readthedocs.io/en/stable/pyomo_modeling_components/Expressions.html"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 51,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Expression to compute the total cooling cost\n",
+ "m.fs.cooling_cost = Expression(\n",
+ " expr=0.25e-7 * (-m.fs.F101.heat_duty[0])\n",
+ " + 0.2e-7 * (-m.fs.D101.condenser.heat_duty[0])\n",
+ ")\n",
+ "\n",
+ "# Expression to compute the total heating cost\n",
+ "m.fs.heating_cost = Expression(\n",
+ " expr=2.2e-7 * m.fs.H101.heat_duty[0]\n",
+ " + 1.2e-7 * m.fs.H102.heat_duty[0]\n",
+ " + 1.9e-7 * m.fs.D101.reboiler.heat_duty[0]\n",
+ ")\n",
+ "\n",
+ "# Expression to compute the total operating cost\n",
+ "m.fs.operating_cost = Expression(\n",
+ " expr=(3600 * 24 * 365 * (m.fs.heating_cost + m.fs.cooling_cost))\n",
+ ")\n",
+ "\n",
+ "# Expression to compute the total capital cost\n",
+ "m.fs.capital_cost = Expression(expr=1e5 * m.fs.R101.volume[0])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Solve the entire flowsheet"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 52,
+ "metadata": {
+ "tags": [
+ "testing"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Check that the degrees of freedom is zero\n",
+ "assert degrees_of_freedom(m) == 0"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 53,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "WARNING: model contains export suffix 'scaling_factor' that contains 7\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 150 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "Ipopt 3.13.2: nlp_scaling_method=gradient-based\n",
+ "tol=1e-06\n",
+ "max_iter=200\n",
+ "\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...: 4042\n",
+ "Number of nonzeros in inequality constraint Jacobian.: 0\n",
+ "Number of nonzeros in Lagrangian Hessian.............: 2376\n",
+ "\n",
+ "Total number of variables............................: 1169\n",
+ " variables with only lower bounds: 112\n",
+ " variables with lower and upper bounds: 365\n",
+ " variables with only upper bounds: 0\n",
+ "Total number of equality constraints.................: 1169\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.83e+04 1.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
+ " 1 0.0000000e+00 8.70e+03 1.50e+03 -1.0 3.69e+04 - 9.71e-01 4.62e-01H 1\n",
+ " 2 0.0000000e+00 1.53e+03 1.56e+03 -1.0 6.75e+03 - 9.77e-01 4.89e-01h 1\n",
+ " 3 0.0000000e+00 1.37e+03 1.55e+05 -1.0 9.37e+03 - 9.90e-01 4.99e-01h 1\n",
+ " 4 0.0000000e+00 6.14e+02 2.31e+09 -1.0 6.09e+03 - 1.00e+00 9.81e-01h 1\n",
+ " 5 0.0000000e+00 5.32e+03 3.62e+10 -1.0 5.56e+02 - 1.00e+00 9.90e-01h 1\n",
+ " 6 0.0000000e+00 1.16e+03 7.80e+09 -1.0 5.36e+00 - 1.00e+00 1.00e+00h 1\n",
+ " 7 0.0000000e+00 5.96e+00 3.64e+07 -1.0 1.47e-03 - 1.00e+00 1.00e+00f 1\n",
+ " 8 0.0000000e+00 1.69e-04 8.15e+02 -1.0 6.77e-06 - 1.00e+00 1.00e+00h 1\n",
+ " 9 0.0000000e+00 7.45e-09 6.64e-03 -3.8 2.00e-07 - 1.00e+00 1.00e+00h 1\n",
+ "Cannot recompute multipliers for feasibility problem. Error in eq_mult_calculator\n",
+ "\n",
+ "Number of Iterations....: 9\n",
+ "\n",
+ " (scaled) (unscaled)\n",
+ "Objective...............: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Dual infeasibility......: 1.5042483516409773e+04 1.5042483516409773e+04\n",
+ "Constraint violation....: 2.9103830456733704e-11 7.4505805969238281e-09\n",
+ "Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Overall NLP error.......: 2.9103830456733704e-11 1.5042483516409773e+04\n",
+ "\n",
+ "\n",
+ "Number of objective function evaluations = 11\n",
+ "Number of objective gradient evaluations = 10\n",
+ "Number of equality constraint evaluations = 11\n",
+ "Number of inequality constraint evaluations = 0\n",
+ "Number of equality constraint Jacobian evaluations = 10\n",
+ "Number of inequality constraint Jacobian evaluations = 0\n",
+ "Number of Lagrangian Hessian evaluations = 9\n",
+ "Total CPU secs in IPOPT (w/o function evaluations) = 0.083\n",
+ "Total CPU secs in NLP function evaluations = 0.013\n",
+ "\n",
+ "EXIT: Optimal Solution Found.\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "{'Problem': [{'Lower bound': -inf, 'Upper bound': inf, 'Number of objectives': 1, 'Number of constraints': 1169, 'Number of variables': 1169, 'Sense': 'unknown'}], 'Solver': [{'Status': 'ok', 'Message': 'Ipopt 3.13.2\\\\x3a Optimal Solution Found', 'Termination condition': 'optimal', 'Id': 0, 'Error rc': 0, 'Time': 0.2022566795349121}], 'Solution': [OrderedDict([('number of solutions', 0), ('number of solutions displayed', 0)])]}"
+ ]
+ },
+ "execution_count": 53,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "solver.solve(m, tee=True)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 54,
+ "metadata": {
+ "tags": [
+ "testing"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Check solver solve status\n",
+ "from pyomo.environ import TerminationCondition\n",
+ "\n",
+ "assert results.solver.termination_condition == TerminationCondition.optimal"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Analyze the Results of the Square Problem\n",
+ "\n",
+ "How much is the total cost (operating cost + capital cost), operating cost, capital cost, benzene purity in the distillate from the distilation column, and conversion of toluene in the reactor?"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 55,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "total cost = $ 442301.47075252194\n",
+ "operating cost = $ 427596.73056805483\n",
+ "capital cost = $ 14704.740184467111\n",
+ "\n",
+ "Distillate flowrate = 0.16196898920633368 mol/s\n",
+ "Benzene purity = 89.4916166580088 %\n",
+ "Residue flowrate = 0.10515007120697904 mol/s\n",
+ "Toluene purity = 43.32260291055251 %\n",
+ "\n",
+ "Conversion = 75.0 %\n",
+ "\n",
+ "Overhead benzene loss in F101 = 42.161938483603194 %\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(\"total cost = $\", value(m.fs.capital_cost) + value(m.fs.operating_cost))\n",
+ "print(\"operating cost = $\", value(m.fs.operating_cost))\n",
+ "print(\"capital cost = $\", value(m.fs.capital_cost))\n",
+ "print()\n",
+ "print(\n",
+ " \"Distillate flowrate = \",\n",
+ " value(m.fs.D101.condenser.distillate.flow_mol[0]()),\n",
+ " \"mol/s\",\n",
+ ")\n",
+ "print(\n",
+ " \"Benzene purity = \",\n",
+ " 100 * value(m.fs.D101.condenser.distillate.mole_frac_comp[0, \"benzene\"]),\n",
+ " \"%\",\n",
+ ")\n",
+ "print(\"Residue flowrate = \", value(m.fs.D101.reboiler.bottoms.flow_mol[0]()), \"mol/s\")\n",
+ "print(\n",
+ " \"Toluene purity = \",\n",
+ " 100 * value(m.fs.D101.reboiler.bottoms.mole_frac_comp[0, \"toluene\"]),\n",
+ " \"%\",\n",
+ ")\n",
+ "print()\n",
+ "print(\"Conversion = \", 100 * value(m.fs.R101.conversion), \"%\")\n",
+ "print()\n",
+ "print(\n",
+ " \"Overhead benzene loss in F101 = \",\n",
+ " 100\n",
+ " * value(m.fs.F101.vap_outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"])\n",
+ " / value(m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]),\n",
+ " \"%\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 56,
+ "metadata": {
+ "tags": [
+ "testing"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "427596.73056805483\n",
+ "14704.740184467111\n"
+ ]
+ }
+ ],
+ "source": [
+ "import pytest\n",
+ "\n",
+ "print(value(m.fs.operating_cost))\n",
+ "assert value(m.fs.operating_cost) == pytest.approx(427596.731, abs=100)\n",
+ "print(value(m.fs.capital_cost))\n",
+ "assert value(m.fs.capital_cost) == pytest.approx(14704.740, abs=100)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Get the state of the streams entering and leaving the reactor R101"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 57,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "====================================================================================\n",
+ "Unit : fs.R101 Time: 0.0\n",
+ "------------------------------------------------------------------------------------\n",
+ " Unit Performance\n",
+ "\n",
+ " Variables: \n",
+ "\n",
+ " Key : Value : Units : Fixed : Bounds\n",
+ " Heat Duty : 0.0000 : watt : True : (None, None)\n",
+ " Volume : 0.14705 : meter ** 3 : False : (None, None)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ " Stream Table\n",
+ " Units Inlet Outlet \n",
+ " flow_mol_phase_comp ('Liq', 'benzene') mole / second 1.2993e-07 1.2993e-07\n",
+ " flow_mol_phase_comp ('Liq', 'toluene') mole / second 8.4147e-07 8.4147e-07\n",
+ " flow_mol_phase_comp ('Liq', 'methane') mole / second 1.0000e-12 1.0000e-12\n",
+ " flow_mol_phase_comp ('Liq', 'hydrogen') mole / second 1.0000e-12 1.0000e-12\n",
+ " flow_mol_phase_comp ('Vap', 'benzene') mole / second 0.11936 0.35374\n",
+ " flow_mol_phase_comp ('Vap', 'toluene') mole / second 0.31252 0.078129\n",
+ " flow_mol_phase_comp ('Vap', 'methane') mole / second 1.0377 1.2721\n",
+ " flow_mol_phase_comp ('Vap', 'hydrogen') mole / second 0.56260 0.32821\n",
+ " temperature kelvin 600.00 771.85\n",
+ " pressure pascal 3.5000e+05 3.5000e+05\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "m.fs.R101.report()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Get the state of the streams entering and leaving the reactor R101"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 58,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "====================================================================================\n",
+ "Unit : fs.F101 Time: 0.0\n",
+ "------------------------------------------------------------------------------------\n",
+ " Unit Performance\n",
+ "\n",
+ " Variables: \n",
+ "\n",
+ " Key : Value : Units : Fixed : Bounds\n",
+ " Heat Duty : -70343. : watt : False : (None, None)\n",
+ " Pressure Change : 0.0000 : pascal : True : (None, None)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ " Stream Table\n",
+ " Units Inlet Vapor Outlet Liquid Outlet\n",
+ " flow_mol_phase_comp ('Liq', 'benzene') mole / second 1.2993e-07 1.0000e-08 0.20460 \n",
+ " flow_mol_phase_comp ('Liq', 'toluene') mole / second 8.4147e-07 1.0000e-08 0.062520 \n",
+ " flow_mol_phase_comp ('Liq', 'methane') mole / second 1.0000e-12 1.0000e-08 2.6712e-07 \n",
+ " flow_mol_phase_comp ('Liq', 'hydrogen') mole / second 1.0000e-12 1.0000e-08 2.6712e-07 \n",
+ " flow_mol_phase_comp ('Vap', 'benzene') mole / second 0.35374 0.14915 1.0000e-08 \n",
+ " flow_mol_phase_comp ('Vap', 'toluene') mole / second 0.078129 0.015610 1.0000e-08 \n",
+ " flow_mol_phase_comp ('Vap', 'methane') mole / second 1.2721 1.2721 1.0000e-08 \n",
+ " flow_mol_phase_comp ('Vap', 'hydrogen') mole / second 0.32821 0.32821 1.0000e-08 \n",
+ " temperature kelvin 771.85 325.00 325.00 \n",
+ " pressure pascal 3.5000e+05 3.5000e+05 3.5000e+05 \n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "m.fs.F101.report()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Next, let's look at how much benzene we are losing with the light gases out of F101. IDAES has tools for creating stream tables based on the `Arcs` and/or `Ports` in a flowsheet. Let us create and print a simple stream table showing the stream leaving the reactor and the vapor stream from F101.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "How much benzene are we losing in the F101 vapor outlet stream?\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 59,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ " Units Reactor Light Gases\n",
+ "flow_mol_phase_comp ('Liq', 'benzene') mole / second 1.2993e-07 1.0000e-08 \n",
+ "flow_mol_phase_comp ('Liq', 'toluene') mole / second 8.4147e-07 1.0000e-08 \n",
+ "flow_mol_phase_comp ('Liq', 'methane') mole / second 1.0000e-12 1.0000e-08 \n",
+ "flow_mol_phase_comp ('Liq', 'hydrogen') mole / second 1.0000e-12 1.0000e-08 \n",
+ "flow_mol_phase_comp ('Vap', 'benzene') mole / second 0.35374 0.14915 \n",
+ "flow_mol_phase_comp ('Vap', 'toluene') mole / second 0.078129 0.015610 \n",
+ "flow_mol_phase_comp ('Vap', 'methane') mole / second 1.2721 1.2721 \n",
+ "flow_mol_phase_comp ('Vap', 'hydrogen') mole / second 0.32821 0.32821 \n",
+ "temperature kelvin 771.85 325.00 \n",
+ "pressure pascal 3.5000e+05 3.5000e+05 \n"
+ ]
+ }
+ ],
+ "source": [
+ "from idaes.core.util.tables import (\n",
+ " create_stream_table_dataframe,\n",
+ " stream_table_dataframe_to_string,\n",
+ ")\n",
+ "\n",
+ "st = create_stream_table_dataframe({\"Reactor\": m.fs.s05, \"Light Gases\": m.fs.s06})\n",
+ "print(stream_table_dataframe_to_string(st))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "You can query additional variables here if you like. \n",
+ "\n",
+ "Use Shift+Enter to run the cell once you have typed in your code. \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Optimization\n",
+ "\n",
+ "\n",
+ "We saw from the results above that the total operating cost for the base case was $442,297 per year. We are producing 0.162 mol/s of benzene at a purity of 89.5%. However, we are losing around 43.3% of benzene in F101 vapor outlet stream. \n",
+ "\n",
+ "Let us try to minimize this cost such that:\n",
+ "- we are producing at least 0.18 mol/s of benzene as distillate i.e. our product stream\n",
+ "- purity of benzene i.e. the mole fraction of benzene in the distillate is at least 99%\n",
+ "- restricting the benzene loss in F101 vapor outlet to less than 20%\n",
+ "\n",
+ "For this problem, our decision variables are as follows:\n",
+ "- H101 outlet temperature\n",
+ "- R101 outlet temperature\n",
+ "- F101 outlet temperature\n",
+ "- H102 outlet temperature\n",
+ "- Condenser pressure\n",
+ "- reflux ratio\n",
+ "- boilup ratio\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let us declare our objective function for this problem. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 60,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.fs.objective = Objective(expr=m.fs.operating_cost + m.fs.capital_cost)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now, we need to unfix the decision variables as we had solved a square problem (degrees of freedom = 0) until now. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 61,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.fs.H101.outlet.temperature.unfix()\n",
+ "m.fs.R101.conversion.unfix()\n",
+ "m.fs.F101.vap_outlet.temperature.unfix()\n",
+ "m.fs.D101.condenser.condenser_pressure.unfix()\n",
+ "m.fs.D101.condenser.reflux_ratio.unfix()\n",
+ "m.fs.D101.reboiler.boilup_ratio.unfix()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "Let us now unfix the remaining variable: the temperature of the outlet from H102\n",
+ "\n",
+ "Use Shift+Enter to run the cell once you have typed in your code. \n",
+ "
\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 63,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Unfix the temperature of the outlet from H102\n",
+ "m.fs.H102.outlet.temperature.unfix()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Next, we need to set bounds on these decision variables to values shown below:\n",
+ "\n",
+ " - H101 outlet temperature [500, 600] K\n",
+ " - R101 outlet temperature [600, 900] K\n",
+ " - F101 outlet temperature [298, 450] K\n",
+ " - H102 outlet temperature [350, 400] K\n",
+ " - D101 condenser pressure [101325, 150000] Pa\n",
+ " - D101 reflux ratio [0.1, 5]\n",
+ " - D101 boilup ratio [0.1, 5]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 64,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Set bounds on the temperature of the outlet from H101\n",
+ "m.fs.H101.outlet.temperature[0].setlb(500)\n",
+ "m.fs.H101.outlet.temperature[0].setub(600)\n",
+ "\n",
+ "# Set bounds on the temperature of the outlet from R101\n",
+ "m.fs.R101.outlet.temperature[0].setlb(600)\n",
+ "m.fs.R101.outlet.temperature[0].setub(900)\n",
+ "\n",
+ "# Set bounds on the volume of the reactor R101\n",
+ "m.fs.R101.volume[0].setlb(0)\n",
+ "\n",
+ "# Set bounds on the temperature of the vapor outlet from F101\n",
+ "m.fs.F101.vap_outlet.temperature[0].setlb(298)\n",
+ "m.fs.F101.vap_outlet.temperature[0].setub(450.0)\n",
+ "\n",
+ "# Set bounds on the temperature of the outlet from H102\n",
+ "m.fs.H102.outlet.temperature[0].setlb(350)\n",
+ "m.fs.H102.outlet.temperature[0].setub(400)\n",
+ "\n",
+ "# Set bounds on the pressure inside the condenser\n",
+ "m.fs.D101.condenser.condenser_pressure.setlb(101325)\n",
+ "m.fs.D101.condenser.condenser_pressure.setub(150000)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "Now, set the bounds for the D101 reflux ratio and boilup ratio.\n",
+ "\n",
+ "Use Shift+Enter to run the cell once you have typed in your code. \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 66,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Set bounds on the reflux ratio\n",
+ "m.fs.D101.condenser.reflux_ratio.setlb(0.1)\n",
+ "m.fs.D101.condenser.reflux_ratio.setub(5)\n",
+ "\n",
+ "# Todo: Set bounds on the boilup ratio\n",
+ "m.fs.D101.reboiler.boilup_ratio.setlb(0.1)\n",
+ "m.fs.D101.reboiler.boilup_ratio.setub(5)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now, the only things left to define are our constraints on overhead loss in F101, distillate flowrate and its purity. Let us first look at defining a constraint for the overhead loss in F101 where we are restricting the benzene leaving the vapor stream to less than 20 % of the benzene available in the reactor outlet. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 67,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Ensure that the overhead loss of benzene from F101 <= 20%\n",
+ "m.fs.overhead_loss = Constraint(\n",
+ " expr=m.fs.F101.vap_outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]\n",
+ " <= 0.20 * m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "Now, add the constraint such that we are producing at least 0.18 mol/s of benzene in the product stream which is the distillate of D101. Let us name this constraint as m.fs.product_flow. \n",
+ "\n",
+ "Use Shift+Enter to run the cell once you have typed in your code. \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 69,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Add minimum product flow constraint\n",
+ "m.fs.product_flow = Constraint(expr=m.fs.D101.condenser.distillate.flow_mol[0] >= 0.18)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let us add the final constraint on product purity or the mole fraction of benzene in the distillate such that it is at least greater than 99%. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 70,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.fs.product_purity = Constraint(\n",
+ " expr=m.fs.D101.condenser.distillate.mole_frac_comp[0, \"benzene\"] >= 0.99\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "We have now defined the optimization problem and we are now ready to solve this problem. \n",
+ "\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 71,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "WARNING: model contains export suffix 'scaling_factor' that contains 3\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 150 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "Ipopt 3.13.2: nlp_scaling_method=gradient-based\n",
+ "tol=1e-06\n",
+ "max_iter=200\n",
+ "\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...: 4073\n",
+ "Number of nonzeros in inequality constraint Jacobian.: 6\n",
+ "Number of nonzeros in Lagrangian Hessian.............: 2391\n",
+ "\n",
+ "Total number of variables............................: 1176\n",
+ " variables with only lower bounds: 113\n",
+ " variables with lower and upper bounds: 372\n",
+ " variables with only upper bounds: 0\n",
+ "Total number of equality constraints.................: 1169\n",
+ "Total number of inequality constraints...............: 3\n",
+ " inequality constraints with only lower bounds: 2\n",
+ " inequality constraints with lower and upper bounds: 0\n",
+ " inequality constraints with only upper bounds: 1\n",
+ "\n",
+ "iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n",
+ " 0 4.4230147e+05 2.99e+05 9.90e+01 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
+ " 1 4.3753585e+05 2.91e+05 1.28e+02 -1.0 3.09e+06 - 3.58e-01 2.40e-02f 1\n",
+ " 2 4.3545100e+05 2.78e+05 1.55e+02 -1.0 1.78e+06 - 3.31e-01 4.74e-02h 1\n",
+ " 3 4.2822311e+05 2.20e+05 4.50e+02 -1.0 2.99e+06 - 2.95e-02 1.35e-01h 1\n",
+ " 4 4.2249096e+05 1.45e+05 1.43e+03 -1.0 7.01e+06 - 5.14e-01 2.03e-01h 1\n",
+ " 5 4.2194364e+05 8.17e+04 1.70e+04 -1.0 6.06e+06 - 5.97e-01 4.28e-01h 1\n",
+ " 6 4.2602765e+05 4.55e+04 1.10e+06 -1.0 4.32e+06 - 9.26e-01 5.07e-01h 1\n",
+ " 7 4.3776643e+05 2.03e+04 6.44e+09 -1.0 2.42e+06 - 9.90e-01 9.47e-01h 1\n",
+ " 8 4.3846260e+05 1.92e+04 6.05e+09 -1.0 4.42e+05 - 5.40e-01 5.74e-02h 1\n",
+ " 9 4.4529853e+05 4.05e+04 4.66e+10 -1.0 2.47e+05 - 9.96e-01 9.90e-01h 1\n",
+ "iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n",
+ " 10 4.4906283e+05 9.76e+03 1.10e+10 -1.0 1.12e+03 -4.0 1.26e-01 7.45e-01h 1\n",
+ " 11 4.5079086e+05 1.19e+03 1.54e+09 -1.0 5.63e+02 -4.5 3.77e-01 1.00e+00h 1\n",
+ " 12 4.5024224e+05 2.66e+00 3.67e+06 -1.0 6.61e+01 -5.0 1.00e+00 1.00e+00f 1\n",
+ " 13 4.4946170e+05 5.64e-01 9.29e+05 -1.0 1.81e+02 -5.4 1.00e+00 7.88e-01f 1\n",
+ " 14 4.4916780e+05 8.48e+00 1.62e+05 -1.0 2.83e+02 -5.9 1.00e+00 1.00e+00f 1\n",
+ " 15 4.4899127e+05 4.83e+00 9.07e+04 -1.0 1.01e+02 -6.4 1.00e+00 4.40e-01f 2\n",
+ " 16 4.4886718e+05 7.00e-01 4.61e+02 -1.0 2.35e+02 -6.9 1.00e+00 1.00e+00f 1\n",
+ " 17 4.4800159e+05 1.39e+02 4.52e+06 -3.8 1.17e+03 -7.3 9.79e-01 9.37e-01f 1\n",
+ " 18 4.4672196e+05 9.59e+02 1.22e+06 -3.8 4.55e+03 -7.8 1.00e+00 9.43e-01f 1\n",
+ " 19 4.4401667e+05 7.75e+03 1.55e+05 -3.8 1.08e+04 -8.3 1.00e+00 1.00e+00f 1\n",
+ "iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n",
+ " 20 4.4185035e+05 1.91e+04 1.36e+04 -3.8 1.33e+04 -8.8 1.00e+00 1.00e+00h 1\n",
+ " 21 4.4241001e+05 3.52e+03 5.96e+03 -3.8 2.94e+03 -9.2 1.00e+00 1.00e+00h 1\n",
+ " 22 4.4185237e+05 7.82e+00 2.91e+02 -3.8 7.13e+03 -9.7 2.39e-01 1.00e+00h 1\n",
+ " 23 4.4124091e+05 1.53e+01 3.11e+02 -3.8 4.82e+04 -10.2 8.59e-01 1.36e-01f 1\n",
+ " 24 4.4137379e+05 1.80e+00 2.91e+02 -3.8 1.41e+04 - 1.95e-01 1.00e+00h 1\n",
+ " 25 4.3862833e+05 1.70e+03 9.48e+04 -3.8 1.57e+07 - 1.29e-03 9.10e-02f 1\n",
+ " 26 4.3883308e+05 1.49e+03 8.46e+04 -3.8 1.02e+06 - 1.00e+00 1.35e-01h 1\n",
+ " 27 4.3885472e+05 2.18e+01 3.40e+03 -3.8 1.38e+05 - 1.00e+00 1.00e+00h 1\n",
+ " 28 4.3884160e+05 5.90e-02 6.38e+01 -3.8 8.66e+03 - 1.00e+00 1.00e+00h 1\n",
+ " 29 4.3884157e+05 6.48e-07 4.63e-04 -3.8 2.89e+01 - 1.00e+00 1.00e+00h 1\n",
+ "iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n",
+ " 30 4.3883990e+05 3.57e-01 2.38e+03 -5.7 8.19e+02 - 1.00e+00 1.00e+00f 1\n",
+ " 31 4.3883992e+05 3.50e-07 7.79e-06 -5.7 3.55e-01 - 1.00e+00 1.00e+00h 1\n",
+ " 32 4.3883990e+05 5.47e-05 3.63e-01 -8.0 1.01e+01 - 1.00e+00 1.00e+00h 1\n",
+ " 33 4.3883990e+05 2.24e-08 1.46e-07 -8.0 5.42e-05 - 1.00e+00 1.00e+00h 1\n",
+ "\n",
+ "Number of Iterations....: 33\n",
+ "\n",
+ " (scaled) (unscaled)\n",
+ "Objective...............: 4.3883989842628603e+02 4.3883989842628600e+05\n",
+ "Dual infeasibility......: 1.4600704448671754e-07 1.4600704448671753e-04\n",
+ "Constraint violation....: 2.9103830456733704e-11 2.2351741790771484e-08\n",
+ "Complementarity.........: 9.0909948039799681e-09 9.0909948039799686e-06\n",
+ "Overall NLP error.......: 9.0909948039799681e-09 1.4600704448671753e-04\n",
+ "\n",
+ "\n",
+ "Number of objective function evaluations = 35\n",
+ "Number of objective gradient evaluations = 34\n",
+ "Number of equality constraint evaluations = 35\n",
+ "Number of inequality constraint evaluations = 35\n",
+ "Number of equality constraint Jacobian evaluations = 34\n",
+ "Number of inequality constraint Jacobian evaluations = 34\n",
+ "Number of Lagrangian Hessian evaluations = 33\n",
+ "Total CPU secs in IPOPT (w/o function evaluations) = 0.164\n",
+ "Total CPU secs in NLP function evaluations = 0.020\n",
+ "\n",
+ "EXIT: Optimal Solution Found.\n"
+ ]
+ }
+ ],
+ "source": [
+ "results = solver.solve(m, tee=True)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 72,
+ "metadata": {
+ "tags": [
+ "testing"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Check solver solve status\n",
+ "from pyomo.environ import TerminationCondition\n",
+ "\n",
+ "assert results.solver.termination_condition == TerminationCondition.optimal"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Optimization Results\n",
+ "\n",
+ "Display the results and product specifications"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 73,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "total cost = $ 438839.898426286\n",
+ "operating cost = $ 408883.5314830889\n",
+ "capital cost = $ 29956.3669431971\n",
+ "\n",
+ "Distillate flowrate = 0.1799999900263989 mol/s\n",
+ "Benzene purity = 98.99999900049086 %\n",
+ "Residue flowrate = 0.1085161642426372 mol/s\n",
+ "Toluene purity = 15.676178086213548 %\n",
+ "\n",
+ "Conversion = 93.38705916369427 %\n",
+ "\n",
+ "Overhead benzene loss in F101 = 17.34061793115618 %\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(\"total cost = $\", value(m.fs.capital_cost) + value(m.fs.operating_cost))\n",
+ "print(\"operating cost = $\", value(m.fs.operating_cost))\n",
+ "print(\"capital cost = $\", value(m.fs.capital_cost))\n",
+ "print()\n",
+ "print(\n",
+ " \"Distillate flowrate = \",\n",
+ " value(m.fs.D101.condenser.distillate.flow_mol[0]()),\n",
+ " \"mol/s\",\n",
+ ")\n",
+ "print(\n",
+ " \"Benzene purity = \",\n",
+ " 100 * value(m.fs.D101.condenser.distillate.mole_frac_comp[0, \"benzene\"]),\n",
+ " \"%\",\n",
+ ")\n",
+ "print(\"Residue flowrate = \", value(m.fs.D101.reboiler.bottoms.flow_mol[0]()), \"mol/s\")\n",
+ "print(\n",
+ " \"Toluene purity = \",\n",
+ " 100 * value(m.fs.D101.reboiler.bottoms.mole_frac_comp[0, \"toluene\"]),\n",
+ " \"%\",\n",
+ ")\n",
+ "print()\n",
+ "print(\"Conversion = \", 100 * value(m.fs.R101.conversion), \"%\")\n",
+ "print()\n",
+ "print(\n",
+ " \"Overhead benzene loss in F101 = \",\n",
+ " 100\n",
+ " * value(m.fs.F101.vap_outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"])\n",
+ " / value(m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]),\n",
+ " \"%\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 74,
+ "metadata": {
+ "tags": [
+ "testing"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "408883.5314830889\n",
+ "29956.3669431971\n"
+ ]
}
+ ],
+ "source": [
+ "import pytest\n",
+ "\n",
+ "print(value(m.fs.operating_cost))\n",
+ "print(value(m.fs.capital_cost))\n",
+ "\n",
+ "assert value(m.fs.operating_cost) == pytest.approx(408883.531, abs=100)\n",
+ "assert value(m.fs.capital_cost) == pytest.approx(29956.367, abs=100)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Display optimal values for the decision variables"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 75,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Optimal Values\n",
+ "\n",
+ "H101 outlet temperature = 568.923204295196 K\n",
+ "\n",
+ "R101 outlet temperature = 790.3655425698853 K\n",
+ "\n",
+ "F101 outlet temperature = 298.0 K\n",
+ "\n",
+ "H102 outlet temperature = 368.7414339952852 K\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(\"Optimal Values\")\n",
+ "print()\n",
+ "\n",
+ "print(\"H101 outlet temperature = \", value(m.fs.H101.outlet.temperature[0]), \"K\")\n",
+ "\n",
+ "print()\n",
+ "print(\"R101 outlet temperature = \", value(m.fs.R101.outlet.temperature[0]), \"K\")\n",
+ "\n",
+ "print()\n",
+ "print(\"F101 outlet temperature = \", value(m.fs.F101.vap_outlet.temperature[0]), \"K\")\n",
+ "\n",
+ "print()\n",
+ "print(\"H102 outlet temperature = \", value(m.fs.H102.outlet.temperature[0]), \"K\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Key Takeaways\n",
+ "\n",
+ "Observe that the optimization was able to reduce the yearly operating cost from \\\\$427,593 to \\\\$408,342 (~4.5%). However, the amortized capital cost more than doubled from \\\\$14,704 to \\\\$29,927 due to the need to increase the conversion in the reactor (from 75% to 93%) to meet the production and purity constraints. \n",
+ "\n",
+ "Further, observe that the product flow rate and product purity are at their minimum values (0.18 mol/s and 99%, respectively). This is expected as increasing recovery would require more energy and cost to purify the product.\n",
+ "\n",
+ "\n",
+ "Finally, observe that the operating temperature of the flash (F101) is almost at its lower bound. This helps in minimizing the amount of benzene in the vapor stream leaving the flash."
+ ]
+ }
+ ],
+ "metadata": {
+ "celltoolbar": "Tags",
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
},
- "nbformat": 4,
- "nbformat_minor": 3
+ "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.10.13"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 3
}
\ No newline at end of file
diff --git a/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_distillation_usr.ipynb b/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_distillation_usr.ipynb
index ecabdf9c..86d59dd3 100644
--- a/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_distillation_usr.ipynb
+++ b/idaes_examples/notebooks/docs/flowsheets/hda_flowsheet_with_distillation_usr.ipynb
@@ -1,1791 +1,3024 @@
{
- "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": [
- "\n",
- "# HDA Flowsheet Simulation and Optimization\n",
- "Maintainer: Brandon Paul \n",
- "Author: Brandon Paul \n",
- "Updated: 2023-06-01 \n",
- "\n",
- "\n",
- "## Note\n",
- "\n",
- "This tutorial will be similar to the HDA flowsheet tutorial in the Tutorials section, except that we use a distillation column instead of a second flash (F102) to produce benzene and toluene products.\n",
- "\n",
- "\n",
- "## Learning outcomes\n",
- "\n",
- "\n",
- "- Construct a steady-state flowsheet using the IDAES unit model library\n",
- "- Connecting unit models in a flowsheet using Arcs\n",
- "- Using the SequentialDecomposition tool to initialize a flowsheet with recycle\n",
- "- Fomulate and solve an optimization problem\n",
- " - Defining an objective function\n",
- " - Setting variable bounds\n",
- " - Adding additional constraints \n",
- "\n",
- "\n",
- "## Problem Statement\n",
- "\n",
- "Hydrodealkylation is a chemical reaction that often involves reacting\n",
- "an aromatic hydrocarbon in the presence of hydrogen gas to form a\n",
- "simpler aromatic hydrocarbon devoid of functional groups. In this\n",
- "example, toluene will be reacted with hydrogen gas at high temperatures\n",
- " to form benzene via the following reaction:\n",
- "\n",
- "**C6H5CH3 + H2 \u2192 C6H6 + CH4**\n",
- "\n",
- "\n",
- "This reaction is often accompanied by an equilibrium side reaction\n",
- "which forms diphenyl, which we will neglect for this example.\n",
- "\n",
- "This example is based on the 1967 AIChE Student Contest problem as\n",
- "present by Douglas, J.M., Chemical Design of Chemical Processes, 1988,\n",
- "McGraw-Hill.\n",
- "\n",
- "The flowsheet that we will be using for this module is shown below with the stream conditions. We will be processing toluene and hydrogen to produce at least 370 TPY of benzene. As shown in the flowsheet, we use a flash tank, F101, to separate out the non-condensibles, and a distillation column, D101, to further separate the benzene-toluene mixture to improve the benzene purity. The non-condensibles separated out in F101 will be partially recycled back to M101 and the rest will be purged. We will assume ideal gas behavior for this flowsheet. The properties required for this module are defined in\n",
- "\n",
- "- `hda_ideal_VLE.py`\n",
- "- `idaes.models.properties.activity_coeff_models.BTX_activity_coeff_VLE`\n",
- "- `hda_reaction.py`\n",
- "\n",
- "We will be using two thermodynamic packages: one (first in the list above) containing all four components (i.e., toluene, hydrogen, benzene, and methane) and the other (second in the list above) containing benzene and toluene only. The latter is needed to simplify the VLE calculations in the distillation column model. \n",
- "\n",
- "![](HDA_flowsheet_distillation.png)\n",
- "\n",
- ""
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Translator block\n",
- "\n",
- "Benzene and toluene are separated by distillation, so the process involves phase equilibrium and two-phase flow conditions. However, the presence of hydrogen and methane complicates the calculations. This is because, hydrogen and methane are non-condensable under all conditions of interest; ergo, a vapor phase will always be present, and the mixture bubble point is extremely low. To simplify the phase equilibrium calculations, hydrogen and methane will be considered completely as non-condensable and insoluble in the liquid outlet from the flash F101.\n",
- "\n",
- "Since no hydrogen and methane will be present in the unit operations following the flash, a different component list can be used to simplify the property calculations. IDAES supports the definition of multiple property packages within a single flowsheet via `Translator` blocks. `Translator` blocks convert between different property calculations, component lists, and equations of state. "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Importing required pyomo and idaes components\n",
- "\n",
- "\n",
- "To construct a flowsheet, we will need several components from the pyomo and idaes package. Let us first import the following components from Pyomo:\n",
- "- Constraint (to write constraints)\n",
- "- Var (to declare variables)\n",
- "- ConcreteModel (to create the concrete model object)\n",
- "- Expression (to evaluate values as a function of variables defined in the model)\n",
- "- Objective (to define an objective function for optimization)\n",
- "- SolverFactory (to solve the problem)\n",
- "- TransformationFactory (to apply certain transformations)\n",
- "- Arc (to connect two unit models)\n",
- "- SequentialDecomposition (to initialize the flowsheet in a sequential mode)\n",
- "\n",
- "For further details on these components, please refer to the pyomo documentation: https://pyomo.readthedocs.io/en/stable/\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "from pyomo.environ import (\n",
- " Constraint,\n",
- " Var,\n",
- " ConcreteModel,\n",
- " Expression,\n",
- " Objective,\n",
- " TransformationFactory,\n",
- " value,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "Import `Arc` and `SequentialDecomposition` tools from `pyomo.network`\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Import the above mentioned tools from pyomo.network"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Import the above mentioned tools from pyomo.network\n",
- "from pyomo.network import Arc, SequentialDecomposition"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "From IDAES, we will be needing the FlowsheetBlock and the following unit models:\n",
- "- Mixer\n",
- "- Heater\n",
- "- CSTR\n",
- "- Flash\n",
- "- Separator (splitter) \n",
- "- PressureChanger\n",
- "- Translator (to switch from one property package to another)\n",
- "- TrayColumn (distillation column)\n",
- "- CondenserType (Type of the overhead condenser: complete or partial)\n",
- "- TemperatureSpec (Temperature specification inside the condenser)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [],
- "source": [
- "from idaes.core import FlowsheetBlock"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {},
- "outputs": [],
- "source": [
- "from idaes.models.unit_models import (\n",
- " PressureChanger,\n",
- " Mixer,\n",
- " Separator as Splitter,\n",
- " Heater,\n",
- " CSTR,\n",
- " Flash,\n",
- " Translator,\n",
- ")\n",
- "\n",
- "from idaes.models_extra.column_models import TrayColumn\n",
- "from idaes.models_extra.column_models.condenser import CondenserType, TemperatureSpec"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We will also be needing some utility tools to put together the flowsheet and calculate the degrees of freedom. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Utility tools to put together the flowsheet and calculate the degrees of freedom\n",
- "from idaes.models.unit_models.pressure_changer import ThermodynamicAssumption\n",
- "from idaes.core.util.model_statistics import degrees_of_freedom\n",
- "from idaes.core.util.initialization import propagate_state\n",
- "from idaes.core.solvers import get_solver\n",
- "import idaes.core.util.scaling as iscale\n",
- "\n",
- "# Import idaes logger to set output levels\n",
- "import idaes.logger as idaeslog"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Importing required thermo and reaction packages\n",
- "\n",
- "Finally, we import the thermophysical (`ideal_VLE.py` and `BTXParameterBlock`) packages and reaction package (`reaction.py`) for the HDA process. We have created custom thermophysical packages that assume ideal gas behavior with support for VLE. The reaction package consists of the stochiometric coefficients for the reaction, heat of reaction, and kinetic information (Arrhenius constant and activation energy). "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "metadata": {},
- "outputs": [],
- "source": [
- "from idaes_examples.mod.hda import hda_reaction as reaction_props\n",
- "from idaes.models.properties.activity_coeff_models.BTX_activity_coeff_VLE import (\n",
- " BTXParameterBlock,\n",
- ")\n",
- "\n",
- "from idaes_examples.mod.hda.hda_ideal_VLE import HDAParameterBlock"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Constructing the Flowsheet\n",
- "\n",
- "We have now imported all the components, unit models, and property modules we need to construct a flowsheet. Let us create a ConcreteModel and add the flowsheet block to it. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Create a Pyomo Concrete Model to contain the problem\n",
- "m = ConcreteModel()\n",
- "\n",
- "# Add a steady state flowsheet block to the model\n",
- "m.fs = FlowsheetBlock(dynamic=False)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We will now add the thermophysical and reaction packages to the flowsheet."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Property package for benzene, toluene, hydrogen, methane mixture\n",
- "m.fs.BTHM_params = HDAParameterBlock()\n",
- "\n",
- "# Property package for the benzene-toluene mixture\n",
- "m.fs.BT_params = BTXParameterBlock(\n",
- " valid_phase=(\"Liq\", \"Vap\"), activity_coeff_model=\"Ideal\"\n",
- ")\n",
- "\n",
- "# Reaction package for the HDA reaction\n",
- "m.fs.reaction_params = reaction_props.HDAReactionParameterBlock(\n",
- " property_package=m.fs.BTHM_params\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Adding Unit Models\n",
- "\n",
- "Let us start adding the unit models we have imported to the flowsheet. Here, we are adding the Mixer (assigned a name M101) and a Heater (assigned a name H101). Note that, all unit models need to be given a property package argument. In addition, the Mixer unit model needs a `list` consisting of the inlets (toluene feed, hydrogen feed and vapor recycle streams in this case). "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 11,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Adding the mixer M101 to the flowsheet\n",
- "m.fs.M101 = Mixer(\n",
- " property_package=m.fs.BTHM_params,\n",
- " inlet_list=[\"toluene_feed\", \"hydrogen_feed\", \"vapor_recycle\"],\n",
- ")\n",
- "\n",
- "# Adding the heater H101 to the flowsheet\n",
- "m.fs.H101 = Heater(property_package=m.fs.BTHM_params, has_phase_equilibrium=True)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "
Inline Exercise:\n",
- "Let us now add the CSTR (assign the name R101) and pass the following arguments:\n",
- "
\n",
- " - \"property_package\": m.fs.BTHM_params
\n",
- " - \"reaction_package\": m.fs.reaction_params
\n",
- " - \"has_heat_of_reaction\": True
\n",
- " - \"has_heat_transfer\": True
\n",
- "
\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 12,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Add reactor with the specifications above"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 13,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Add reactor with the specifications above\n",
- "m.fs.R101 = CSTR(\n",
- " property_package=m.fs.BTHM_params,\n",
- " reaction_package=m.fs.reaction_params,\n",
- " has_heat_of_reaction=True,\n",
- " has_heat_transfer=True,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Let us now add the Flash (assign the name F101), Splitter (assign the name S101) and PressureChanger (assign the name C101)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 14,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Adding the flash tank F101 to the flowsheet\n",
- "m.fs.F101 = Flash(\n",
- " property_package=m.fs.BTHM_params, has_heat_transfer=True, has_pressure_change=True\n",
- ")\n",
- "\n",
- "# Adding the splitter S101 to the flowsheet\n",
- "m.fs.S101 = Splitter(\n",
- " property_package=m.fs.BTHM_params, outlet_list=[\"purge\", \"recycle\"]\n",
- ")\n",
- "\n",
- "# Adding the compressor C101 to the flowsheet\n",
- "m.fs.C101 = PressureChanger(\n",
- " property_package=m.fs.BTHM_params,\n",
- " compressor=True,\n",
- " thermodynamic_assumption=ThermodynamicAssumption.isothermal,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Remark\n",
- "\n",
- "Currently, the `SequentialDecomposition()` tool, which we will later be using to initialize the flowsheet, does not support the distillation column model. Thus, we will first simulate the flowsheet without the distillation column. After it converges, we will then add the distillation column, initialize it, and simulate the entire flowsheet."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "As mentioned above, we use the `m.fs.BTHM_params` package, which contains all the four species, for the reactor loop, and the simpler `m.fs.BT_params` for unit operations following the flash (i.e., heater H102 and the distillation column D101). We define a `Translator` block to link the source property package and the package it is to be translated to in the following manner:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 15,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Add translator block to convert between property packages\n",
- "m.fs.translator = Translator(\n",
- " inlet_property_package=m.fs.BTHM_params, outlet_property_package=m.fs.BT_params\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Translator block constraints\n",
- "\n",
- "The `Translator` block needs to know how to translate between the two property packages. This must be custom coded for each application because of the generality of the IDAES framework.\n",
- "\n",
- "For this process, five constraints are required based on the state variables used in the outgoing process.\n",
- "\n",
- "- Since we assumed that only benzene and toluene are present in the liquid phase, the total molar flowrate must be the sum of molar flowrates of benzene and toluene, respectively.\n",
- "- Temperature of the inlet and outlet streams must be the same.\n",
- "- Pressure of the inlet and outgoing streams must be the same\n",
- "- The mole fraction of benzene in the outgoing stream is the ratio of the molar flowrate of liquid benzene in the inlet to the sum of molar flowrates of liquid benzene and toluene in the inlet.\n",
- "- The mole fraction of toluene in the outgoing stream is the ratio of the molar flowrate of liquid toluene in the inlet to the sum of molar flowrates of liquid benzene and toluene in the inlet."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 16,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Add constraint: Total flow = benzene flow + toluene flow (molar)\n",
- "m.fs.translator.eq_total_flow = Constraint(\n",
- " expr=m.fs.translator.outlet.flow_mol[0]\n",
- " == m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
- " + m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
- ")\n",
- "\n",
- "# Add constraint: Outlet temperature = Inlet temperature\n",
- "m.fs.translator.eq_temperature = Constraint(\n",
- " expr=m.fs.translator.outlet.temperature[0] == m.fs.translator.inlet.temperature[0]\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "In the above, note that the variable flow_mol_phase_comp has the index - [time, phase, component]. As this is a steady-state flowsheet, the time index by default is 0. The valid phases are [\"Liq\", \"Vap\"]. Similarly the valid component list is [\"benzene\", \"toluene\", \"hydrogen\", \"methane\"]."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "Add the constraint to ensure that the outlet pressure is the same as the inlet pressure\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 17,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Add constraint: Outlet pressure = Inlet pressure"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 18,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Add constraint: Outlet pressure = Inlet pressure\n",
- "m.fs.translator.eq_pressure = Constraint(\n",
- " expr=m.fs.translator.outlet.pressure[0] == m.fs.translator.inlet.pressure[0]\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 19,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Remaining constraints on the translator block\n",
- "\n",
- "# Add constraint: Benzene mole fraction definition\n",
- "m.fs.translator.eq_mole_frac_benzene = Constraint(\n",
- " expr=m.fs.translator.outlet.mole_frac_comp[0, \"benzene\"]\n",
- " == m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
- " / (\n",
- " m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
- " + m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
- " )\n",
- ")\n",
- "\n",
- "# Add constraint: Toluene mole fraction definition\n",
- "m.fs.translator.eq_mole_frac_toluene = Constraint(\n",
- " expr=m.fs.translator.outlet.mole_frac_comp[0, \"toluene\"]\n",
- " == m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
- " / (\n",
- " m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
- " + m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
- " )\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "Finally, let us add the Heater H102 in the same way as H101 but pass the m.fs.BT_params thermodynamic package. We will add the distillation column after converging the flowsheet.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 20,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Add the Heater H102 to the flowsheet"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 21,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Add the Heater H102 to the flowsheet\n",
- "m.fs.H102 = Heater(\n",
- " property_package=m.fs.BT_params,\n",
- " has_pressure_change=True,\n",
- " has_phase_equilibrium=True,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Connecting Unit Models using Arcs\n",
- "\n",
- "We have now added the initial set of unit models to the flowsheet. However, we have not yet specifed how the units are connected. To do this, we will be using the `Arc` which is a pyomo component that takes in two arguments: `source` and `destination`. Let us connect the outlet of the mixer (M101) to the inlet of the heater (H101). "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 22,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.s03 = Arc(source=m.fs.M101.outlet, destination=m.fs.H101.inlet)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "![](HDA_flowsheet_distillation.png) \n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "Now, connect the H101 outlet to the R101 inlet using the cell above as a guide. \n",
- "
\n",
- "\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 23,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Connect the H101 outlet to R101 inlet"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 24,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Connect the H101 outlet to R101 inlet\n",
- "m.fs.s04 = Arc(source=m.fs.H101.outlet, destination=m.fs.R101.inlet)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We will now be connecting the rest of the units as shown below. Notice how the outlet names are different for the flash tank as it has a vapor and a liquid outlet. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 25,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.s05 = Arc(source=m.fs.R101.outlet, destination=m.fs.F101.inlet)\n",
- "m.fs.s06 = Arc(source=m.fs.F101.vap_outlet, destination=m.fs.S101.inlet)\n",
- "m.fs.s08 = Arc(source=m.fs.S101.recycle, destination=m.fs.C101.inlet)\n",
- "m.fs.s09 = Arc(source=m.fs.C101.outlet, destination=m.fs.M101.vapor_recycle)\n",
- "m.fs.s10a = Arc(source=m.fs.F101.liq_outlet, destination=m.fs.translator.inlet)\n",
- "m.fs.s10b = Arc(source=m.fs.translator.outlet, destination=m.fs.H102.inlet)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We have now connected the unit model block using the arcs. However, each of these arcs link to ports on the two unit models that are connected. In this case, the ports consist of the state variables that need to be linked between the unit models. Pyomo provides a convenient method to write these equality constraints for us between two ports and this is done as follows:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 26,
- "metadata": {},
- "outputs": [],
- "source": [
- "TransformationFactory(\"network.expand_arcs\").apply_to(m)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Appending additional constraints to the model\n",
- "\n",
- "Now, we will see how we can add additional constraints to the model using `Constraint` from Pyomo.\n",
- "\n",
- "Consider the reactor R101. By default, the conversion of a component is not calculated when we simulate the flowsheet. If we are interested either in specifying or constraining the conversion value, we can add the following constraint to calculate the conversion:\n",
- "$$ \\text{Conversion of toluene} = \\frac{\\text{molar flow of toluene in the inlet} - \\text{molar flow of toluene in the outlet}}{\\text{molar flow of toluene in the inlet}} $$ \n",
- "\n",
- "We add the constraint to the model as shown below."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 27,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Define the conversion variables using 'Var'\n",
- "m.fs.R101.conversion = Var(initialize=0.75, bounds=(0, 1))\n",
- "\n",
- "# Append the constraint to the model\n",
- "m.fs.R101.conv_constraint = Constraint(\n",
- " expr=m.fs.R101.conversion * m.fs.R101.inlet.flow_mol_phase_comp[0, \"Vap\", \"toluene\"]\n",
- " == (\n",
- " m.fs.R101.inlet.flow_mol_phase_comp[0, \"Vap\", \"toluene\"]\n",
- " - m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"toluene\"]\n",
- " )\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Fixing feed conditions and Initializing the flowsheet\n",
- "\n",
- "Let us first check how many degrees of freedom exist for this flowsheet using the `degrees_of_freedom` tool we imported earlier. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 28,
- "metadata": {},
- "outputs": [],
- "source": [
- "print(degrees_of_freedom(m))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We will now be fixing the toluene feed stream to the conditions shown in the flowsheet above. Please note that though this is a pure toluene feed, the remaining components are still assigned a very small non-zero value to help with convergence and initializing. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 30,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"benzene\"].fix(1e-5)\n",
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"toluene\"].fix(1e-5)\n",
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"hydrogen\"].fix(1e-5)\n",
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"methane\"].fix(1e-5)\n",
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"benzene\"].fix(1e-5)\n",
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"toluene\"].fix(0.30)\n",
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"hydrogen\"].fix(1e-5)\n",
- "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"methane\"].fix(1e-5)\n",
- "m.fs.M101.toluene_feed.temperature.fix(303.2)\n",
- "m.fs.M101.toluene_feed.pressure.fix(350000)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Similarly, let us fix the hydrogen feed to the following conditions in the next cell:\n",
- " \n",
- " - FH2 = 0.30 mol/s
\n",
- " - FCH4 = 0.02 mol/s
\n",
- " - Remaining components = 1e-5 mol/s
\n",
- " - T = 303.2 K
\n",
- " - P = 350000 Pa
\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 31,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"benzene\"].fix(1e-5)\n",
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"toluene\"].fix(1e-5)\n",
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"hydrogen\"].fix(0.30)\n",
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"methane\"].fix(0.02)\n",
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"benzene\"].fix(1e-5)\n",
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"toluene\"].fix(1e-5)\n",
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"hydrogen\"].fix(1e-5)\n",
- "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"methane\"].fix(1e-5)\n",
- "m.fs.M101.hydrogen_feed.temperature.fix(303.2)\n",
- "m.fs.M101.hydrogen_feed.pressure.fix(350000)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Fixing unit model specifications\n",
- "\n",
- "Now that we have fixed our inlet feed conditions, we will now be fixing the operating conditions for the unit models in the flowsheet. Let us set the H101 outlet temperature to 600 K. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 32,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Fix the temperature of the outlet from the heater H101\n",
- "m.fs.H101.outlet.temperature.fix(600)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "
Inline Exercise:\n",
- "Set the conditions for the reactor R101 to the following conditions:\n",
- "
\n",
- " - `conversion` = 0.75
\n",
- " - `heat_duty` = 0
\n",
- "
\n",
- "\n",
- "Use Shift+Enter to run the cell once you have typed in your code. \n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 33,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Fix the 'conversion' of the reactor R101\n",
- "\n",
- "\n",
- "# Todo: Fix the 'heat_duty' of the reactor R101"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 34,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Fix the 'conversion' of the reactor R101\n",
- "m.fs.R101.conversion.fix(0.75)\n",
- "\n",
- "# Todo: Fix the 'heat_duty' of the reactor R101\n",
- "m.fs.R101.heat_duty.fix(0)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The Flash conditions for F101 can be set as follows. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 35,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Fix the temperature of the vapor outlet from F101\n",
- "m.fs.F101.vap_outlet.temperature.fix(325.0)\n",
- "\n",
- "# Fix the pressure drop in the flash F101\n",
- "m.fs.F101.deltaP.fix(0)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Let us fix the split fraction of the purge stream from the splitter S101 and the outlet pressure from the compressor C101"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 36,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Fix the split fraction of the 'purge' stream from S101\n",
- "m.fs.S101.split_fraction[0, \"purge\"].fix(0.2)\n",
- "\n",
- "# Fix the pressure of the outlet from the compressor C101\n",
- "m.fs.C101.outlet.pressure.fix(350000)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Finally, let us fix the temperature of the outlet from H102 and the pressure drop in H102 as the following"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 37,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Fix the temperature of the outlet from the heater H102\n",
- "m.fs.H102.outlet.temperature.fix(375)\n",
- "\n",
- "# Fix the pressure drop in the heater H102\n",
- "m.fs.H102.deltaP.fix(-200000)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "To avoid convergence issues associated with poorly scaled variables and/or constraints, we scale the variables and constraints corresponding to the heaters H101 and H102, flash F101 and the reactor R101. Scaling factors for the flow rates, temperature, pressure, etc. have been defined in the property package: `ideal_VLE.py` file. Here, we set scaling factors only for the heat duty of the heater, the reaction extent, heat duty and volume of the reactor."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 38,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Set scaling factors for heat duty, reaction extent and volume\n",
- "iscale.set_scaling_factor(m.fs.H101.control_volume.heat, 1e-2)\n",
- "iscale.set_scaling_factor(m.fs.R101.control_volume.heat, 1e-2)\n",
- "iscale.set_scaling_factor(m.fs.R101.control_volume.rate_reaction_extent, 1)\n",
- "iscale.set_scaling_factor(m.fs.R101.control_volume.volume, 1)\n",
- "iscale.set_scaling_factor(m.fs.F101.control_volume.heat, 1e-2)\n",
- "iscale.set_scaling_factor(m.fs.H102.control_volume.heat, 1e-2)\n",
- "\n",
- "# Set the scaling factors for the remaining variables and all constraints\n",
- "iscale.calculate_scaling_factors(m.fs.H101)\n",
- "iscale.calculate_scaling_factors(m.fs.R101)\n",
- "iscale.calculate_scaling_factors(m.fs.F101)\n",
- "iscale.calculate_scaling_factors(m.fs.H102)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "We have now defined all the feed conditions and the inputs required for the unit models. The system should now have 0 degrees of freedom i.e. should be a square problem. Please check that the degrees of freedom is 0. \n",
- "\n",
- "Use Shift+Enter to run the cell once you have typed in your code. \n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 39,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Check the degrees of freedom"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 40,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Check the degrees of freedom\n",
- "print(degrees_of_freedom(m))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Initialization\n",
- "\n",
- "This subsection will demonstrate how to use the built-in sequential decomposition tool to initialize our flowsheet.\n",
- "\n",
- "Let us first create an object for the `SequentialDecomposition` and specify our options for this. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 42,
- "metadata": {},
- "outputs": [],
- "source": [
- "seq = SequentialDecomposition()\n",
- "seq.options.select_tear_method = \"heuristic\"\n",
- "seq.options.tear_method = \"Wegstein\"\n",
- "seq.options.iterLim = 3\n",
- "\n",
- "# Using the SD tool\n",
- "G = seq.create_graph(m)\n",
- "heuristic_tear_set = seq.tear_set_arcs(G, method=\"heuristic\")\n",
- "order = seq.calculation_order(G)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Which is the tear stream? Display tear set and order"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 43,
- "metadata": {},
- "outputs": [],
- "source": [
- "for o in heuristic_tear_set:\n",
- " print(o.name)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "What sequence did the SD tool determine to solve this flowsheet with the least number of tears? "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 44,
- "metadata": {},
- "outputs": [],
- "source": [
- "for o in order:\n",
- " print(o[0].name)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The SequentialDecomposition tool has determined that the tear stream is the mixer outlet (s03 in the Figure above). We will need to provide a reasonable guess for this.\n",
- "\n",
- "For the initial guess, we assume that the flowrate of the recycle stream (s09) is zero. Consequently, the flow rate of the stream s03 is simply the sum of the flowrates of the toluene feed and hydrogen feed streams. Further, since the temperature and the pressure of both the toluene and hydrogen feed streams are the same, we specify their values as the initial guess for the temperature and pressure of the stream s03."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 45,
- "metadata": {},
- "outputs": [],
- "source": [
- "tear_guesses = {\n",
- " \"flow_mol_phase_comp\": {\n",
- " (0, \"Vap\", \"benzene\"): 1e-5,\n",
- " (0, \"Vap\", \"toluene\"): 1e-5,\n",
- " (0, \"Vap\", \"hydrogen\"): 0.30,\n",
- " (0, \"Vap\", \"methane\"): 0.02,\n",
- " (0, \"Liq\", \"benzene\"): 1e-5,\n",
- " (0, \"Liq\", \"toluene\"): 0.30,\n",
- " (0, \"Liq\", \"hydrogen\"): 1e-5,\n",
- " (0, \"Liq\", \"methane\"): 1e-5,\n",
- " },\n",
- " \"temperature\": {0: 303},\n",
- " \"pressure\": {0: 350000},\n",
- "}\n",
- "\n",
- "# Pass the tear_guess to the SD tool\n",
- "seq.set_guesses_for(m.fs.H101.inlet, tear_guesses)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Next, we need to tell the tool how to initialize a particular unit. We will be writing a python function which takes in a \"unit\" and calls the initialize method on that unit."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 46,
- "metadata": {},
- "outputs": [],
- "source": [
- "def function(unit):\n",
- " unit.initialize(outlvl=idaeslog.INFO)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We are now ready to initialize our flowsheet in a sequential mode. Note that we specifically set the iteration limit to be 3 as we are trying to use this tool only to get a good set of initial values such that IPOPT can then take over and solve this flowsheet for us. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 47,
- "metadata": {
- "scrolled": false
- },
- "outputs": [],
- "source": [
- "seq.run(m, function)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "We have now initialized the flowsheet. Let us run the flowsheet in a simulation mode to look at the results. \n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 48,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Create the solver object\n",
- "solver = get_solver()\n",
- "\n",
- "# Solve the model\n",
- "results = solver.solve(m, tee=True)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Add distillation column \n",
- "\n",
- "As mentioned earlier, the `SequentialDecomposition` tool currently does not support the distillation column model. Thus, we have not included the distillation column in the flowsheet. Now that we have a converged flowsheet, we will add the distillation column and simulate the entire flowsheet. \n",
- "\n",
- "In the following, we will\n",
- "- Add the distillation column \n",
- "- Connect it to the heater \n",
- "- Add the necessary equality constraints\n",
- "- Propogate the state variable information from the outlet of the heater to the inlet of the distillation column \n",
- "- Fix the degrees of freedom of the distillation block (reflux ratio, boilup ratio, and condenser pressure)\n",
- "- Scale the control volume heat variables to help convergence\n",
- "- Initialize the distillation block.\n",
- "\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 50,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Add distillation column to the flowsheet\n",
- "m.fs.D101 = TrayColumn(\n",
- " number_of_trays=10,\n",
- " feed_tray_location=5,\n",
- " condenser_type=CondenserType.totalCondenser,\n",
- " condenser_temperature_spec=TemperatureSpec.atBubblePoint,\n",
- " property_package=m.fs.BT_params,\n",
- ")\n",
- "\n",
- "# Connect the outlet from the heater H102 to the distillation column\n",
- "m.fs.s11 = Arc(source=m.fs.H102.outlet, destination=m.fs.D101.feed)\n",
- "\n",
- "# Add the necessary equality constraints\n",
- "TransformationFactory(\"network.expand_arcs\").apply_to(m)\n",
- "\n",
- "# Propagate the state\n",
- "propagate_state(m.fs.s11)\n",
- "\n",
- "# Fix the reflux ratio, boilup ratio, and the condenser pressure\n",
- "m.fs.D101.condenser.reflux_ratio.fix(0.5)\n",
- "m.fs.D101.reboiler.boilup_ratio.fix(0.5)\n",
- "m.fs.D101.condenser.condenser_pressure.fix(150000)\n",
- "\n",
- "# set scaling factors\n",
- "# Set scaling factors for heat duty\n",
- "iscale.set_scaling_factor(m.fs.D101.condenser.control_volume.heat, 1e-2)\n",
- "iscale.set_scaling_factor(m.fs.D101.reboiler.control_volume.heat, 1e-2)\n",
- "\n",
- "# Set the scaling factors for the remaining variables and all constraints\n",
- "iscale.calculate_scaling_factors(m.fs.D101)\n",
- "\n",
- "# Initialize the distillation column\n",
- "m.fs.D101.initialize(outlvl=idaeslog.INFO)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Adding expressions to compute capital and operating costs\n",
- "\n",
- "In this section, we will add a few Expressions that allow us to evaluate the performance. Expressions provide a convenient way of calculating certain values that are a function of the variables defined in the model. For more details on Expressions, please refer to: https://pyomo.readthedocs.io/en/stable/pyomo_modeling_components/Expressions.html"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 51,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Expression to compute the total cooling cost\n",
- "m.fs.cooling_cost = Expression(\n",
- " expr=0.25e-7 * (-m.fs.F101.heat_duty[0])\n",
- " + 0.2e-7 * (-m.fs.D101.condenser.heat_duty[0])\n",
- ")\n",
- "\n",
- "# Expression to compute the total heating cost\n",
- "m.fs.heating_cost = Expression(\n",
- " expr=2.2e-7 * m.fs.H101.heat_duty[0]\n",
- " + 1.2e-7 * m.fs.H102.heat_duty[0]\n",
- " + 1.9e-7 * m.fs.D101.reboiler.heat_duty[0]\n",
- ")\n",
- "\n",
- "# Expression to compute the total operating cost\n",
- "m.fs.operating_cost = Expression(\n",
- " expr=(3600 * 24 * 365 * (m.fs.heating_cost + m.fs.cooling_cost))\n",
- ")\n",
- "\n",
- "# Expression to compute the total capital cost\n",
- "m.fs.capital_cost = Expression(expr=1e5 * m.fs.R101.volume[0])"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Solve the entire flowsheet"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 53,
- "metadata": {},
- "outputs": [],
- "source": [
- "solver.solve(m, tee=True)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Analyze the Results of the Square Problem\n",
- "\n",
- "How much is the total cost (operating cost + capital cost), operating cost, capital cost, benzene purity in the distillate from the distilation column, and conversion of toluene in the reactor?"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 55,
- "metadata": {},
- "outputs": [],
- "source": [
- "print(\"total cost = $\", value(m.fs.capital_cost) + value(m.fs.operating_cost))\n",
- "print(\"operating cost = $\", value(m.fs.operating_cost))\n",
- "print(\"capital cost = $\", value(m.fs.capital_cost))\n",
- "print()\n",
- "print(\n",
- " \"Distillate flowrate = \",\n",
- " value(m.fs.D101.condenser.distillate.flow_mol[0]()),\n",
- " \"mol/s\",\n",
- ")\n",
- "print(\n",
- " \"Benzene purity = \",\n",
- " 100 * value(m.fs.D101.condenser.distillate.mole_frac_comp[0, \"benzene\"]),\n",
- " \"%\",\n",
- ")\n",
- "print(\"Residue flowrate = \", value(m.fs.D101.reboiler.bottoms.flow_mol[0]()), \"mol/s\")\n",
- "print(\n",
- " \"Toluene purity = \",\n",
- " 100 * value(m.fs.D101.reboiler.bottoms.mole_frac_comp[0, \"toluene\"]),\n",
- " \"%\",\n",
- ")\n",
- "print()\n",
- "print(\"Conversion = \", 100 * value(m.fs.R101.conversion), \"%\")\n",
- "print()\n",
- "print(\n",
- " \"Overhead benzene loss in F101 = \",\n",
- " 100\n",
- " * value(m.fs.F101.vap_outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"])\n",
- " / value(m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]),\n",
- " \"%\",\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Get the state of the streams entering and leaving the reactor R101"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 57,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.R101.report()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Get the state of the streams entering and leaving the reactor R101"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 58,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.F101.report()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Next, let's look at how much benzene we are losing with the light gases out of F101. IDAES has tools for creating stream tables based on the `Arcs` and/or `Ports` in a flowsheet. Let us create and print a simple stream table showing the stream leaving the reactor and the vapor stream from F101.\n",
- "\n",
- "\n",
- "Inline Exercise:\n",
- "How much benzene are we losing in the F101 vapor outlet stream?\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 59,
- "metadata": {},
- "outputs": [],
- "source": [
- "from idaes.core.util.tables import (\n",
- " create_stream_table_dataframe,\n",
- " stream_table_dataframe_to_string,\n",
- ")\n",
- "\n",
- "st = create_stream_table_dataframe({\"Reactor\": m.fs.s05, \"Light Gases\": m.fs.s06})\n",
- "print(stream_table_dataframe_to_string(st))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "You can querry additional variables here if you like. \n",
- "\n",
- "Use Shift+Enter to run the cell once you have typed in your code. \n",
- "
"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Optimization\n",
- "\n",
- "\n",
- "We saw from the results above that the total operating cost for the base case was $442,297 per year. We are producing 0.162 mol/s of benzene at a purity of 89.5%. However, we are losing around 43.3% of benzene in F101 vapor outlet stream. \n",
- "\n",
- "Let us try to minimize this cost such that:\n",
- "- we are producing at least 0.18 mol/s of benzene as distillate i.e. our product stream\n",
- "- purity of benzene i.e. the mole fraction of benzene in the distillate is at least 99%\n",
- "- restricting the benzene loss in F101 vapor outlet to less than 20%\n",
- "\n",
- "For this problem, our decision variables are as follows:\n",
- "- H101 outlet temperature\n",
- "- R101 outlet temperature\n",
- "- F101 outlet temperature\n",
- "- H102 outlet temperature\n",
- "- Condenser pressure\n",
- "- reflux ratio\n",
- "- boilup ratio\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Let us declare our objective function for this problem. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 60,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.objective = Objective(expr=m.fs.operating_cost + m.fs.capital_cost)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Now, we need to unfix the decision variables as we had solved a square problem (degrees of freedom = 0) until now. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 61,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.H101.outlet.temperature.unfix()\n",
- "m.fs.R101.conversion.unfix()\n",
- "m.fs.F101.vap_outlet.temperature.unfix()\n",
- "m.fs.D101.condenser.condenser_pressure.unfix()\n",
- "m.fs.D101.condenser.reflux_ratio.unfix()\n",
- "m.fs.D101.reboiler.boilup_ratio.unfix()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "Let us now unfix the remaining variable: the temperature of the outlet from H102\n",
- "\n",
- "Use Shift+Enter to run the cell once you have typed in your code. \n",
- "
\n",
- "\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 62,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Unfix the temperature of the outlet from H102"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 63,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Unfix the temperature of the outlet from H102\n",
- "m.fs.H102.outlet.temperature.unfix()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Next, we need to set bounds on these decision variables to values shown below:\n",
- "\n",
- " - H101 outlet temperature [500, 600] K\n",
- " - R101 outlet temperature [600, 900] K\n",
- " - F101 outlet temperature [298, 450] K\n",
- " - H102 outlet temperature [350, 400] K\n",
- " - D101 condenser pressure [101325, 150000] Pa\n",
- " - D101 reflux ratio [0.1, 5]\n",
- " - D101 boilup ratio [0.1, 5]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 64,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Set bounds on the temperature of the outlet from H101\n",
- "m.fs.H101.outlet.temperature[0].setlb(500)\n",
- "m.fs.H101.outlet.temperature[0].setub(600)\n",
- "\n",
- "# Set bounds on the temperature of the outlet from R101\n",
- "m.fs.R101.outlet.temperature[0].setlb(600)\n",
- "m.fs.R101.outlet.temperature[0].setub(900)\n",
- "\n",
- "# Set bounds on the volume of the reactor R101\n",
- "m.fs.R101.volume[0].setlb(0)\n",
- "\n",
- "# Set bounds on the temperature of the vapor outlet from F101\n",
- "m.fs.F101.vap_outlet.temperature[0].setlb(298)\n",
- "m.fs.F101.vap_outlet.temperature[0].setub(450.0)\n",
- "\n",
- "# Set bounds on the temperature of the outlet from H102\n",
- "m.fs.H102.outlet.temperature[0].setlb(350)\n",
- "m.fs.H102.outlet.temperature[0].setub(400)\n",
- "\n",
- "# Set bounds on the pressure inside the condenser\n",
- "m.fs.D101.condenser.condenser_pressure.setlb(101325)\n",
- "m.fs.D101.condenser.condenser_pressure.setub(150000)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "Now, set the bounds for the D101 reflux ratio and boilup ratio.\n",
- "\n",
- "Use Shift+Enter to run the cell once you have typed in your code. \n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 65,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Set bounds on the reflux ratio\n",
- "\n",
- "\n",
- "# Todo: Set bounds on the boilup ratio"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 66,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Set bounds on the reflux ratio\n",
- "m.fs.D101.condenser.reflux_ratio.setlb(0.1)\n",
- "m.fs.D101.condenser.reflux_ratio.setub(5)\n",
- "\n",
- "# Todo: Set bounds on the boilup ratio\n",
- "m.fs.D101.reboiler.boilup_ratio.setlb(0.1)\n",
- "m.fs.D101.reboiler.boilup_ratio.setub(5)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Now, the only things left to define are our constraints on overhead loss in F101, distillate flowrate and its purity. Let us first look at defining a constraint for the overhead loss in F101 where we are restricting the benzene leaving the vapor stream to less than 20 % of the benzene available in the reactor outlet. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 67,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Ensure that the overhead loss of benzene from F101 <= 20%\n",
- "m.fs.overhead_loss = Constraint(\n",
- " expr=m.fs.F101.vap_outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]\n",
- " <= 0.20 * m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "Inline Exercise:\n",
- "Now, add the constraint such that we are producing at least 0.18 mol/s of benzene in the product stream which is the distillate of D101. Let us name this constraint as m.fs.product_flow. \n",
- "\n",
- "Use Shift+Enter to run the cell once you have typed in your code. \n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 68,
- "metadata": {
- "tags": [
- "exercise"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Add minimum product flow constraint"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 69,
- "metadata": {
- "tags": [
- "solution"
- ]
- },
- "outputs": [],
- "source": [
- "# Todo: Add minimum product flow constraint\n",
- "m.fs.product_flow = Constraint(expr=m.fs.D101.condenser.distillate.flow_mol[0] >= 0.18)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Let us add the final constraint on product purity or the mole fraction of benzene in the distillate such that it is at least greater than 99%. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 70,
- "metadata": {},
- "outputs": [],
- "source": [
- "m.fs.product_purity = Constraint(\n",
- " expr=m.fs.D101.condenser.distillate.mole_frac_comp[0, \"benzene\"] >= 0.99\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "We have now defined the optimization problem and we are now ready to solve this problem. \n",
- "\n",
- "\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 71,
- "metadata": {},
- "outputs": [],
- "source": [
- "results = solver.solve(m, tee=True)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Optimization Results\n",
- "\n",
- "Display the results and product specifications"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 73,
- "metadata": {},
- "outputs": [],
- "source": [
- "print(\"total cost = $\", value(m.fs.capital_cost) + value(m.fs.operating_cost))\n",
- "print(\"operating cost = $\", value(m.fs.operating_cost))\n",
- "print(\"capital cost = $\", value(m.fs.capital_cost))\n",
- "print()\n",
- "print(\n",
- " \"Distillate flowrate = \",\n",
- " value(m.fs.D101.condenser.distillate.flow_mol[0]()),\n",
- " \"mol/s\",\n",
- ")\n",
- "print(\n",
- " \"Benzene purity = \",\n",
- " 100 * value(m.fs.D101.condenser.distillate.mole_frac_comp[0, \"benzene\"]),\n",
- " \"%\",\n",
- ")\n",
- "print(\"Residue flowrate = \", value(m.fs.D101.reboiler.bottoms.flow_mol[0]()), \"mol/s\")\n",
- "print(\n",
- " \"Toluene purity = \",\n",
- " 100 * value(m.fs.D101.reboiler.bottoms.mole_frac_comp[0, \"toluene\"]),\n",
- " \"%\",\n",
- ")\n",
- "print()\n",
- "print(\"Conversion = \", 100 * value(m.fs.R101.conversion), \"%\")\n",
- "print()\n",
- "print(\n",
- " \"Overhead benzene loss in F101 = \",\n",
- " 100\n",
- " * value(m.fs.F101.vap_outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"])\n",
- " / value(m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]),\n",
- " \"%\",\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Display optimal values for the decision variables"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 75,
- "metadata": {},
- "outputs": [],
- "source": [
- "print(\"Optimal Values\")\n",
- "print()\n",
- "\n",
- "print(\"H101 outlet temperature = \", value(m.fs.H101.outlet.temperature[0]), \"K\")\n",
- "\n",
- "print()\n",
- "print(\"R101 outlet temperature = \", value(m.fs.R101.outlet.temperature[0]), \"K\")\n",
- "\n",
- "print()\n",
- "print(\"F101 outlet temperature = \", value(m.fs.F101.vap_outlet.temperature[0]), \"K\")\n",
- "\n",
- "print()\n",
- "print(\"H102 outlet temperature = \", value(m.fs.H102.outlet.temperature[0]), \"K\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Key Takeaways\n",
- "\n",
- "Observe that the optimization was able to reduce the yearly operating cost from \\\\$427,593 to \\\\$408,342 (~4.5%). However, the amortized capital cost more than doubled from \\\\$14,704 to \\\\$29,927 due to the need to increase the conversion in the reactor (from 75% to 93%) to meet the production and purity constraints. \n",
- "\n",
- "Further, observe that the product flow rate and product purity are at their minimum values (0.18 mol/s and 99%, respectively). This is expected as increasing recovery would require more energy and cost to purify the product.\n",
- "\n",
- "\n",
- "Finally, observe that the operating temperature of the flash (F101) is almost at its lower bound. This helps in minimizing the amount of benzene in the vapor stream leaving the flash."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
+ "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": [
+ "\n",
+ "# HDA Flowsheet Simulation and Optimization\n",
+ "Maintainer: Brandon Paul \n",
+ "Author: Brandon Paul \n",
+ "Updated: 2023-06-01 \n",
+ "\n",
+ "\n",
+ "## Note\n",
+ "\n",
+ "This tutorial will be similar to the HDA flowsheet tutorial in the Tutorials section, except that we use a distillation column instead of a second flash (F102) to produce benzene and toluene products.\n",
+ "\n",
+ "\n",
+ "## Learning outcomes\n",
+ "\n",
+ "\n",
+ "- Construct a steady-state flowsheet using the IDAES unit model library\n",
+ "- Connecting unit models in a flowsheet using Arcs\n",
+ "- Using the SequentialDecomposition tool to initialize a flowsheet with recycle\n",
+ "- Fomulate and solve an optimization problem\n",
+ " - Defining an objective function\n",
+ " - Setting variable bounds\n",
+ " - Adding additional constraints \n",
+ "\n",
+ "\n",
+ "## Problem Statement\n",
+ "\n",
+ "Hydrodealkylation is a chemical reaction that often involves reacting\n",
+ "an aromatic hydrocarbon in the presence of hydrogen gas to form a\n",
+ "simpler aromatic hydrocarbon devoid of functional groups. In this\n",
+ "example, toluene will be reacted with hydrogen gas at high temperatures\n",
+ " to form benzene via the following reaction:\n",
+ "\n",
+ "**C6H5CH3 + H2 → C6H6 + CH4**\n",
+ "\n",
+ "\n",
+ "This reaction is often accompanied by an equilibrium side reaction\n",
+ "which forms diphenyl, which we will neglect for this example.\n",
+ "\n",
+ "This example is based on the 1967 AIChE Student Contest problem as\n",
+ "present by Douglas, J.M., Chemical Design of Chemical Processes, 1988,\n",
+ "McGraw-Hill.\n",
+ "\n",
+ "The flowsheet that we will be using for this module is shown below with the stream conditions. We will be processing toluene and hydrogen to produce at least 370 TPY of benzene. As shown in the flowsheet, we use a flash tank, F101, to separate out the non-condensibles, and a distillation column, D101, to further separate the benzene-toluene mixture to improve the benzene purity. The non-condensibles separated out in F101 will be partially recycled back to M101 and the rest will be purged. We will assume ideal gas behavior for this flowsheet. The properties required for this module are defined in\n",
+ "\n",
+ "- `hda_ideal_VLE.py`\n",
+ "- `idaes.models.properties.activity_coeff_models.BTX_activity_coeff_VLE`\n",
+ "- `hda_reaction.py`\n",
+ "\n",
+ "We will be using two thermodynamic packages: one (first in the list above) containing all four components (i.e., toluene, hydrogen, benzene, and methane) and the other (second in the list above) containing benzene and toluene only. The latter is needed to simplify the VLE calculations in the distillation column model. \n",
+ "\n",
+ "![](HDA_flowsheet_distillation.png)\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Translator block\n",
+ "\n",
+ "Benzene and toluene are separated by distillation, so the process involves phase equilibrium and two-phase flow conditions. However, the presence of hydrogen and methane complicates the calculations. This is because, hydrogen and methane are non-condensable under all conditions of interest; ergo, a vapor phase will always be present, and the mixture bubble point is extremely low. To simplify the phase equilibrium calculations, hydrogen and methane will be considered completely as non-condensable and insoluble in the liquid outlet from the flash F101.\n",
+ "\n",
+ "Since no hydrogen and methane will be present in the unit operations following the flash, a different component list can be used to simplify the property calculations. IDAES supports the definition of multiple property packages within a single flowsheet via `Translator` blocks. `Translator` blocks convert between different property calculations, component lists, and equations of state. "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Importing required pyomo and idaes components\n",
+ "\n",
+ "\n",
+ "To construct a flowsheet, we will need several components from the pyomo and idaes package. Let us first import the following components from Pyomo:\n",
+ "- Constraint (to write constraints)\n",
+ "- Var (to declare variables)\n",
+ "- ConcreteModel (to create the concrete model object)\n",
+ "- Expression (to evaluate values as a function of variables defined in the model)\n",
+ "- Objective (to define an objective function for optimization)\n",
+ "- SolverFactory (to solve the problem)\n",
+ "- TransformationFactory (to apply certain transformations)\n",
+ "- Arc (to connect two unit models)\n",
+ "- SequentialDecomposition (to initialize the flowsheet in a sequential mode)\n",
+ "\n",
+ "For further details on these components, please refer to the pyomo documentation: https://pyomo.readthedocs.io/en/stable/\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from pyomo.environ import (\n",
+ " Constraint,\n",
+ " Var,\n",
+ " ConcreteModel,\n",
+ " Expression,\n",
+ " Objective,\n",
+ " TransformationFactory,\n",
+ " value,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "Import `Arc` and `SequentialDecomposition` tools from `pyomo.network`\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Import the above mentioned tools from pyomo.network"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Import the above mentioned tools from pyomo.network\n",
+ "from pyomo.network import Arc, SequentialDecomposition"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "From IDAES, we will be needing the FlowsheetBlock and the following unit models:\n",
+ "- Mixer\n",
+ "- Heater\n",
+ "- CSTR\n",
+ "- Flash\n",
+ "- Separator (splitter) \n",
+ "- PressureChanger\n",
+ "- Translator (to switch from one property package to another)\n",
+ "- TrayColumn (distillation column)\n",
+ "- CondenserType (Type of the overhead condenser: complete or partial)\n",
+ "- TemperatureSpec (Temperature specification inside the condenser)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from idaes.core import FlowsheetBlock"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from idaes.models.unit_models import (\n",
+ " PressureChanger,\n",
+ " Mixer,\n",
+ " Separator as Splitter,\n",
+ " Heater,\n",
+ " CSTR,\n",
+ " Flash,\n",
+ " Translator,\n",
+ ")\n",
+ "\n",
+ "from idaes.models_extra.column_models import TrayColumn\n",
+ "from idaes.models_extra.column_models.condenser import CondenserType, TemperatureSpec"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We will also be needing some utility tools to put together the flowsheet and calculate the degrees of freedom. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Utility tools to put together the flowsheet and calculate the degrees of freedom\n",
+ "from idaes.models.unit_models.pressure_changer import ThermodynamicAssumption\n",
+ "from idaes.core.util.model_statistics import degrees_of_freedom\n",
+ "from idaes.core.util.initialization import propagate_state\n",
+ "from idaes.core.solvers import get_solver\n",
+ "import idaes.core.util.scaling as iscale\n",
+ "from idaes.core.util.exceptions import InitializationError\n",
+ "\n",
+ "# Import idaes logger to set output levels\n",
+ "import idaes.logger as idaeslog"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Importing required thermo and reaction packages\n",
+ "\n",
+ "Finally, we import the thermophysical (`ideal_VLE.py` and `BTXParameterBlock`) packages and reaction package (`reaction.py`) for the HDA process. We have created custom thermophysical packages that assume ideal gas behavior with support for VLE. The reaction package consists of the stochiometric coefficients for the reaction, heat of reaction, and kinetic information (Arrhenius constant and activation energy). "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from idaes_examples.mod.hda import hda_reaction as reaction_props\n",
+ "from idaes.models.properties.activity_coeff_models.BTX_activity_coeff_VLE import (\n",
+ " BTXParameterBlock,\n",
+ ")\n",
+ "\n",
+ "from idaes_examples.mod.hda.hda_ideal_VLE import HDAParameterBlock"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Constructing the Flowsheet\n",
+ "\n",
+ "We have now imported all the components, unit models, and property modules we need to construct a flowsheet. Let us create a ConcreteModel and add the flowsheet block to it. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Create a Pyomo Concrete Model to contain the problem\n",
+ "m = ConcreteModel()\n",
+ "\n",
+ "# Add a steady state flowsheet block to the model\n",
+ "m.fs = FlowsheetBlock(dynamic=False)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We will now add the thermophysical and reaction packages to the flowsheet."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Property package for benzene, toluene, hydrogen, methane mixture\n",
+ "m.fs.BTHM_params = HDAParameterBlock()\n",
+ "\n",
+ "# Property package for the benzene-toluene mixture\n",
+ "m.fs.BT_params = BTXParameterBlock(\n",
+ " valid_phase=(\"Liq\", \"Vap\"), activity_coeff_model=\"Ideal\"\n",
+ ")\n",
+ "\n",
+ "# Reaction package for the HDA reaction\n",
+ "m.fs.reaction_params = reaction_props.HDAReactionParameterBlock(\n",
+ " property_package=m.fs.BTHM_params\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Adding Unit Models\n",
+ "\n",
+ "Let us start adding the unit models we have imported to the flowsheet. Here, we are adding the Mixer (assigned a name M101) and a Heater (assigned a name H101). Note that, all unit models need to be given a property package argument. In addition, the Mixer unit model needs a `list` consisting of the inlets (toluene feed, hydrogen feed and vapor recycle streams in this case). "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Adding the mixer M101 to the flowsheet\n",
+ "m.fs.M101 = Mixer(\n",
+ " property_package=m.fs.BTHM_params,\n",
+ " inlet_list=[\"toluene_feed\", \"hydrogen_feed\", \"vapor_recycle\"],\n",
+ ")\n",
+ "\n",
+ "# Adding the heater H101 to the flowsheet\n",
+ "m.fs.H101 = Heater(property_package=m.fs.BTHM_params, has_phase_equilibrium=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "
Inline Exercise:\n",
+ "Let us now add the CSTR (assign the name R101) and pass the following arguments:\n",
+ "
\n",
+ " - \"property_package\": m.fs.BTHM_params
\n",
+ " - \"reaction_package\": m.fs.reaction_params
\n",
+ " - \"has_heat_of_reaction\": True
\n",
+ " - \"has_heat_transfer\": True
\n",
+ "
\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Add reactor with the specifications above"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Add reactor with the specifications above\n",
+ "m.fs.R101 = CSTR(\n",
+ " property_package=m.fs.BTHM_params,\n",
+ " reaction_package=m.fs.reaction_params,\n",
+ " has_heat_of_reaction=True,\n",
+ " has_heat_transfer=True,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let us now add the Flash (assign the name F101), Splitter (assign the name S101) and PressureChanger (assign the name C101)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Adding the flash tank F101 to the flowsheet\n",
+ "m.fs.F101 = Flash(\n",
+ " property_package=m.fs.BTHM_params, has_heat_transfer=True, has_pressure_change=True\n",
+ ")\n",
+ "\n",
+ "# Adding the splitter S101 to the flowsheet\n",
+ "m.fs.S101 = Splitter(\n",
+ " property_package=m.fs.BTHM_params, outlet_list=[\"purge\", \"recycle\"]\n",
+ ")\n",
+ "\n",
+ "# Adding the compressor C101 to the flowsheet\n",
+ "m.fs.C101 = PressureChanger(\n",
+ " property_package=m.fs.BTHM_params,\n",
+ " compressor=True,\n",
+ " thermodynamic_assumption=ThermodynamicAssumption.isothermal,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Remark\n",
+ "\n",
+ "Currently, the `SequentialDecomposition()` tool, which we will later be using to initialize the flowsheet, does not support the distillation column model. Thus, we will first simulate the flowsheet without the distillation column. After it converges, we will then add the distillation column, initialize it, and simulate the entire flowsheet."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As mentioned above, we use the `m.fs.BTHM_params` package, which contains all the four species, for the reactor loop, and the simpler `m.fs.BT_params` for unit operations following the flash (i.e., heater H102 and the distillation column D101). We define a `Translator` block to link the source property package and the package it is to be translated to in the following manner:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Add translator block to convert between property packages\n",
+ "m.fs.translator = Translator(\n",
+ " inlet_property_package=m.fs.BTHM_params, outlet_property_package=m.fs.BT_params\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Translator block constraints\n",
+ "\n",
+ "The `Translator` block needs to know how to translate between the two property packages. This must be custom coded for each application because of the generality of the IDAES framework.\n",
+ "\n",
+ "For this process, five constraints are required based on the state variables used in the outgoing process.\n",
+ "\n",
+ "- Since we assumed that only benzene and toluene are present in the liquid phase, the total molar flowrate must be the sum of molar flowrates of benzene and toluene, respectively.\n",
+ "- Temperature of the inlet and outlet streams must be the same.\n",
+ "- Pressure of the inlet and outgoing streams must be the same\n",
+ "- The mole fraction of benzene in the outgoing stream is the ratio of the molar flowrate of liquid benzene in the inlet to the sum of molar flowrates of liquid benzene and toluene in the inlet.\n",
+ "- The mole fraction of toluene in the outgoing stream is the ratio of the molar flowrate of liquid toluene in the inlet to the sum of molar flowrates of liquid benzene and toluene in the inlet."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Add constraint: Total flow = benzene flow + toluene flow (molar)\n",
+ "m.fs.translator.eq_total_flow = Constraint(\n",
+ " expr=m.fs.translator.outlet.flow_mol[0]\n",
+ " == m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
+ " + m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
+ ")\n",
+ "\n",
+ "# Add constraint: Outlet temperature = Inlet temperature\n",
+ "m.fs.translator.eq_temperature = Constraint(\n",
+ " expr=m.fs.translator.outlet.temperature[0] == m.fs.translator.inlet.temperature[0]\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In the above, note that the variable flow_mol_phase_comp has the index - [time, phase, component]. As this is a steady-state flowsheet, the time index by default is 0. The valid phases are [\"Liq\", \"Vap\"]. Similarly the valid component list is [\"benzene\", \"toluene\", \"hydrogen\", \"methane\"]."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "Add the constraint to ensure that the outlet pressure is the same as the inlet pressure\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Add constraint: Outlet pressure = Inlet pressure"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Add constraint: Outlet pressure = Inlet pressure\n",
+ "m.fs.translator.eq_pressure = Constraint(\n",
+ " expr=m.fs.translator.outlet.pressure[0] == m.fs.translator.inlet.pressure[0]\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Remaining constraints on the translator block\n",
+ "\n",
+ "# Add constraint: Benzene mole fraction definition\n",
+ "m.fs.translator.eq_mole_frac_benzene = Constraint(\n",
+ " expr=m.fs.translator.outlet.mole_frac_comp[0, \"benzene\"]\n",
+ " == m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
+ " / (\n",
+ " m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
+ " + m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
+ " )\n",
+ ")\n",
+ "\n",
+ "# Add constraint: Toluene mole fraction definition\n",
+ "m.fs.translator.eq_mole_frac_toluene = Constraint(\n",
+ " expr=m.fs.translator.outlet.mole_frac_comp[0, \"toluene\"]\n",
+ " == m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
+ " / (\n",
+ " m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"benzene\"]\n",
+ " + m.fs.translator.inlet.flow_mol_phase_comp[0, \"Liq\", \"toluene\"]\n",
+ " )\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "Finally, let us add the Heater H102 in the same way as H101 but pass the m.fs.BT_params thermodynamic package. We will add the distillation column after converging the flowsheet.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 20,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Add the Heater H102 to the flowsheet"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Add the Heater H102 to the flowsheet\n",
+ "m.fs.H102 = Heater(\n",
+ " property_package=m.fs.BT_params,\n",
+ " has_pressure_change=True,\n",
+ " has_phase_equilibrium=True,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Connecting Unit Models using Arcs\n",
+ "\n",
+ "We have now added the initial set of unit models to the flowsheet. However, we have not yet specified how the units are connected. To do this, we will be using the `Arc` which is a pyomo component that takes in two arguments: `source` and `destination`. Let us connect the outlet of the mixer (M101) to the inlet of the heater (H101). "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.fs.s03 = Arc(source=m.fs.M101.outlet, destination=m.fs.H101.inlet)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "![](HDA_flowsheet_distillation.png) \n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "Now, connect the H101 outlet to the R101 inlet using the cell above as a guide. \n",
+ "
\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 23,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Connect the H101 outlet to R101 inlet"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Connect the H101 outlet to R101 inlet\n",
+ "m.fs.s04 = Arc(source=m.fs.H101.outlet, destination=m.fs.R101.inlet)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We will now be connecting the rest of the units as shown below. Notice how the outlet names are different for the flash tank as it has a vapor and a liquid outlet. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.fs.s05 = Arc(source=m.fs.R101.outlet, destination=m.fs.F101.inlet)\n",
+ "m.fs.s06 = Arc(source=m.fs.F101.vap_outlet, destination=m.fs.S101.inlet)\n",
+ "m.fs.s08 = Arc(source=m.fs.S101.recycle, destination=m.fs.C101.inlet)\n",
+ "m.fs.s09 = Arc(source=m.fs.C101.outlet, destination=m.fs.M101.vapor_recycle)\n",
+ "m.fs.s10a = Arc(source=m.fs.F101.liq_outlet, destination=m.fs.translator.inlet)\n",
+ "m.fs.s10b = Arc(source=m.fs.translator.outlet, destination=m.fs.H102.inlet)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We have now connected the unit model block using the arcs. However, each of these arcs link to ports on the two unit models that are connected. In this case, the ports consist of the state variables that need to be linked between the unit models. Pyomo provides a convenient method to write these equality constraints for us between two ports and this is done as follows:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 26,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "TransformationFactory(\"network.expand_arcs\").apply_to(m)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Appending additional constraints to the model\n",
+ "\n",
+ "Now, we will see how we can add additional constraints to the model using `Constraint` from Pyomo.\n",
+ "\n",
+ "Consider the reactor R101. By default, the conversion of a component is not calculated when we simulate the flowsheet. If we are interested either in specifying or constraining the conversion value, we can add the following constraint to calculate the conversion:\n",
+ "$$ \\text{Conversion of toluene} = \\frac{\\text{molar flow of toluene in the inlet} - \\text{molar flow of toluene in the outlet}}{\\text{molar flow of toluene in the inlet}} $$ \n",
+ "\n",
+ "We add the constraint to the model as shown below."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 27,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Define the conversion variables using 'Var'\n",
+ "m.fs.R101.conversion = Var(initialize=0.75, bounds=(0, 1))\n",
+ "\n",
+ "# Append the constraint to the model\n",
+ "m.fs.R101.conv_constraint = Constraint(\n",
+ " expr=m.fs.R101.conversion * m.fs.R101.inlet.flow_mol_phase_comp[0, \"Vap\", \"toluene\"]\n",
+ " == (\n",
+ " m.fs.R101.inlet.flow_mol_phase_comp[0, \"Vap\", \"toluene\"]\n",
+ " - m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"toluene\"]\n",
+ " )\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Fixing feed conditions and Initializing the flowsheet\n",
+ "\n",
+ "Let us first check how many degrees of freedom exist for this flowsheet using the `degrees_of_freedom` tool we imported earlier. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 28,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "29\n"
+ ]
}
- ],
- "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.8.16"
+ ],
+ "source": [
+ "print(degrees_of_freedom(m))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We will now be fixing the toluene feed stream to the conditions shown in the flowsheet above. Please note that though this is a pure toluene feed, the remaining components are still assigned a very small non-zero value to help with convergence and initializing. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 30,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"benzene\"].fix(1e-5)\n",
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"toluene\"].fix(1e-5)\n",
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"hydrogen\"].fix(1e-5)\n",
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Vap\", \"methane\"].fix(1e-5)\n",
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"benzene\"].fix(1e-5)\n",
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"toluene\"].fix(0.30)\n",
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"hydrogen\"].fix(1e-5)\n",
+ "m.fs.M101.toluene_feed.flow_mol_phase_comp[0, \"Liq\", \"methane\"].fix(1e-5)\n",
+ "m.fs.M101.toluene_feed.temperature.fix(303.2)\n",
+ "m.fs.M101.toluene_feed.pressure.fix(350000)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Similarly, let us fix the hydrogen feed to the following conditions in the next cell:\n",
+ " \n",
+ " - FH2 = 0.30 mol/s
\n",
+ " - FCH4 = 0.02 mol/s
\n",
+ " - Remaining components = 1e-5 mol/s
\n",
+ " - T = 303.2 K
\n",
+ " - P = 350000 Pa
\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 31,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"benzene\"].fix(1e-5)\n",
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"toluene\"].fix(1e-5)\n",
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"hydrogen\"].fix(0.30)\n",
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Vap\", \"methane\"].fix(0.02)\n",
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"benzene\"].fix(1e-5)\n",
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"toluene\"].fix(1e-5)\n",
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"hydrogen\"].fix(1e-5)\n",
+ "m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, \"Liq\", \"methane\"].fix(1e-5)\n",
+ "m.fs.M101.hydrogen_feed.temperature.fix(303.2)\n",
+ "m.fs.M101.hydrogen_feed.pressure.fix(350000)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Fixing unit model specifications\n",
+ "\n",
+ "Now that we have fixed our inlet feed conditions, we will now be fixing the operating conditions for the unit models in the flowsheet. Let us set the H101 outlet temperature to 600 K. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 32,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Fix the temperature of the outlet from the heater H101\n",
+ "m.fs.H101.outlet.temperature.fix(600)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "
Inline Exercise:\n",
+ "Set the conditions for the reactor R101 to the following conditions:\n",
+ "
\n",
+ " - `conversion` = 0.75
\n",
+ " - `heat_duty` = 0
\n",
+ "
\n",
+ "\n",
+ "Use Shift+Enter to run the cell once you have typed in your code. \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 33,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Fix the 'conversion' of the reactor R101\n",
+ "\n",
+ "\n",
+ "# Todo: Fix the 'heat_duty' of the reactor R101"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 34,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Fix the 'conversion' of the reactor R101\n",
+ "m.fs.R101.conversion.fix(0.75)\n",
+ "\n",
+ "# Todo: Fix the 'heat_duty' of the reactor R101\n",
+ "m.fs.R101.heat_duty.fix(0)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The Flash conditions for F101 can be set as follows. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 35,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Fix the temperature of the vapor outlet from F101\n",
+ "m.fs.F101.vap_outlet.temperature.fix(325.0)\n",
+ "\n",
+ "# Fix the pressure drop in the flash F101\n",
+ "m.fs.F101.deltaP.fix(0)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let us fix the split fraction of the purge stream from the splitter S101 and the outlet pressure from the compressor C101"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 36,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Fix the split fraction of the 'purge' stream from S101\n",
+ "m.fs.S101.split_fraction[0, \"purge\"].fix(0.2)\n",
+ "\n",
+ "# Fix the pressure of the outlet from the compressor C101\n",
+ "m.fs.C101.outlet.pressure.fix(350000)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Finally, let us fix the temperature of the outlet from H102 and the pressure drop in H102 as the following"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 37,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Fix the temperature of the outlet from the heater H102\n",
+ "m.fs.H102.outlet.temperature.fix(375)\n",
+ "\n",
+ "# Fix the pressure drop in the heater H102\n",
+ "m.fs.H102.deltaP.fix(-200000)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "To avoid convergence issues associated with poorly scaled variables and/or constraints, we scale the variables and constraints corresponding to the heaters H101 and H102, flash F101 and the reactor R101. Scaling factors for the flow rates, temperature, pressure, etc. have been defined in the property package: `ideal_VLE.py` file. Here, we set scaling factors only for the heat duty of the heater, the reaction extent, heat duty and volume of the reactor."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 38,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enth_mol_phase_comp[Liq,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enth_mol_phase_comp[Liq,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enth_mol_phase_comp[Vap,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enth_mol_phase_comp[Vap,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].enth_mol_phase_comp[Liq,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].enth_mol_phase_comp[Liq,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].enth_mol_phase_comp[Vap,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].enth_mol_phase_comp[Vap,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].material_flow_terms[Liq,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].material_flow_terms[Vap,benzene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].material_flow_terms[Liq,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].material_flow_terms[Vap,toluene]\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enthalpy_flow_terms[Liq], enthalpy_flow_terms\n",
+ "2024-08-28 18:38:14 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.H102.control_volume.properties_in[0.0].enthalpy_flow_terms[Vap], enthalpy_flow_terms\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Set scaling factors for heat duty, reaction extent and volume\n",
+ "iscale.set_scaling_factor(m.fs.H101.control_volume.heat, 1e-2)\n",
+ "iscale.set_scaling_factor(m.fs.R101.control_volume.heat, 1e-2)\n",
+ "iscale.set_scaling_factor(m.fs.R101.control_volume.rate_reaction_extent, 1)\n",
+ "iscale.set_scaling_factor(m.fs.R101.control_volume.volume, 1)\n",
+ "iscale.set_scaling_factor(m.fs.F101.control_volume.heat, 1e-2)\n",
+ "iscale.set_scaling_factor(m.fs.H102.control_volume.heat, 1e-2)\n",
+ "\n",
+ "# Set the scaling factors for the remaining variables and all constraints\n",
+ "iscale.calculate_scaling_factors(m.fs.H101)\n",
+ "iscale.calculate_scaling_factors(m.fs.R101)\n",
+ "iscale.calculate_scaling_factors(m.fs.F101)\n",
+ "iscale.calculate_scaling_factors(m.fs.H102)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "We have now defined all the feed conditions and the inputs required for the unit models. The system should now have 0 degrees of freedom i.e. should be a square problem. Please check that the degrees of freedom is 0. \n",
+ "\n",
+ "Use Shift+Enter to run the cell once you have typed in your code. \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 39,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Check the degrees of freedom"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 40,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "0\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Todo: Check the degrees of freedom\n",
+ "print(degrees_of_freedom(m))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Initialization\n",
+ "\n",
+ "This subsection will demonstrate how to use the built-in sequential decomposition tool to initialize our flowsheet.\n",
+ "\n",
+ "Let us first create an object for the `SequentialDecomposition` and specify our options for this. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 42,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "seq = SequentialDecomposition()\n",
+ "seq.options.select_tear_method = \"heuristic\"\n",
+ "seq.options.tear_method = \"Wegstein\"\n",
+ "seq.options.iterLim = 3\n",
+ "\n",
+ "# Using the SD tool\n",
+ "G = seq.create_graph(m)\n",
+ "heuristic_tear_set = seq.tear_set_arcs(G, method=\"heuristic\")\n",
+ "order = seq.calculation_order(G)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Which is the tear stream? Display tear set and order"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 43,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "fs.s03\n"
+ ]
}
+ ],
+ "source": [
+ "for o in heuristic_tear_set:\n",
+ " print(o.name)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "What sequence did the SD tool determine to solve this flowsheet with the least number of tears? "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 44,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "fs.H101\n",
+ "fs.R101\n",
+ "fs.F101\n",
+ "fs.S101\n",
+ "fs.C101\n",
+ "fs.M101\n"
+ ]
+ }
+ ],
+ "source": [
+ "for o in order:\n",
+ " print(o[0].name)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The SequentialDecomposition tool has determined that the tear stream is the mixer outlet (s03 in the Figure above). We will need to provide a reasonable guess for this.\n",
+ "\n",
+ "For the initial guess, we assume that the flowrate of the recycle stream (s09) is zero. Consequently, the flow rate of the stream s03 is simply the sum of the flowrates of the toluene feed and hydrogen feed streams. Further, since the temperature and the pressure of both the toluene and hydrogen feed streams are the same, we specify their values as the initial guess for the temperature and pressure of the stream s03."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 45,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "tear_guesses = {\n",
+ " \"flow_mol_phase_comp\": {\n",
+ " (0, \"Vap\", \"benzene\"): 1e-5,\n",
+ " (0, \"Vap\", \"toluene\"): 1e-5,\n",
+ " (0, \"Vap\", \"hydrogen\"): 0.30,\n",
+ " (0, \"Vap\", \"methane\"): 0.02,\n",
+ " (0, \"Liq\", \"benzene\"): 1e-5,\n",
+ " (0, \"Liq\", \"toluene\"): 0.30,\n",
+ " (0, \"Liq\", \"hydrogen\"): 1e-5,\n",
+ " (0, \"Liq\", \"methane\"): 1e-5,\n",
+ " },\n",
+ " \"temperature\": {0: 303.2},\n",
+ " \"pressure\": {0: 350000},\n",
+ "}\n",
+ "\n",
+ "# Pass the tear_guess to the SD tool\n",
+ "seq.set_guesses_for(m.fs.H101.inlet, tear_guesses)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Next, we need to tell the tool how to initialize a particular unit. We will be writing a python function which takes in a \"unit\" and calls the initialize method on that unit. For the initialization, we will import a Block Triangularization Initializer which decomposes the model into a set of subproblems. These subproblems are solved using a block triangularization transformation before applying a simple Newton or user-selected solver. Methods such as block triangularization often solve faster and yield more reliable behavior than heuristic methods, but sometime struggle to decompose models with strongly coupled equations (e.g. column models, systems with counter-current flow, vapor-liquid equilibrium)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 46,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def function(unit):\n",
+ " # Try initializing using default initializer,\n",
+ " # if it fails (probably due to scaling) try for the second time\n",
+ " try:\n",
+ " initializer = unit.default_initializer()\n",
+ " initializer.initialize(unit, output_level=idaeslog.INFO)\n",
+ " except InitializationError:\n",
+ " solver = get_solver()\n",
+ " solver.solve(unit)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We are now ready to initialize our flowsheet in a sequential mode. Note that we specifically set the iteration limit to be 3 as we are trying to use this tool only to get a good set of initial values such that IPOPT can then take over and solve this flowsheet for us. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 47,
+ "metadata": {
+ "scrolled": false
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2024-08-28 18:38:14 [INFO] idaes.init.fs.H101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:16 [INFO] idaes.init.fs.H101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:16 [INFO] idaes.init.fs.R101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:16 [INFO] idaes.init.fs.R101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.F101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.F101.control_volume.properties_out: Initialization Complete\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 12\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 50 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.S101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:17 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - \n",
+ "2024-08-28 18:38:18 [INFO] idaes.init.fs.C101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:18 [INFO] idaes.init.fs.C101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:18 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 1 optimal - .\n",
+ "2024-08-28 18:38:18 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 2 optimal - .\n",
+ "2024-08-28 18:38:19 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 3 optimal - .\n",
+ "2024-08-28 18:38:19 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 4 optimal - .\n",
+ "2024-08-28 18:38:19 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 5 optimal - .\n",
+ "2024-08-28 18:38:19 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 1 optimal - .\n",
+ "2024-08-28 18:38:19 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 2 optimal - .\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 3 optimal - .\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 4 optimal - .\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 5 optimal - .\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.M101.toluene_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.M101.hydrogen_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:20 [INFO] idaes.init.fs.M101.vapor_recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:21 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:21 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - \n",
+ "2024-08-28 18:38:21 [INFO] idaes.init.fs.H101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:21 [INFO] idaes.init.fs.H101.control_volume.properties_out: Initialization Complete\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 11\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 50 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.R101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.R101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.F101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.F101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.S101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:22 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - \n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.C101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.C101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.M101.toluene_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.M101.hydrogen_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.M101.vapor_recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:23 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - \n",
+ "2024-08-28 18:38:24 [INFO] idaes.init.fs.H101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:24 [INFO] idaes.init.fs.H101.control_volume.properties_out: Initialization Complete\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 11\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 50 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "2024-08-28 18:38:24 [INFO] idaes.init.fs.R101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:24 [INFO] idaes.init.fs.R101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.F101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.F101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.S101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - \n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.C101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:25 [INFO] idaes.init.fs.C101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.M101.toluene_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.M101.hydrogen_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.M101.vapor_recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - \n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.H101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:26 [INFO] idaes.init.fs.H101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.R101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.R101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.F101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.F101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.S101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:27 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - \n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.C101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.C101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.M101.toluene_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.M101.hydrogen_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.M101.vapor_recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:28 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - \n",
+ "2024-08-28 18:38:29 [INFO] idaes.init.fs.H101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:29 [INFO] idaes.init.fs.H101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:29 [INFO] idaes.init.fs.R101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:29 [INFO] idaes.init.fs.R101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.F101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.F101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.S101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.S101.purge_state: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.S101.recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.S101: Initialization Step 2 Complete: optimal - \n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.C101.control_volume.properties_in: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.C101.control_volume.properties_out: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.M101.toluene_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:30 [INFO] idaes.init.fs.M101.hydrogen_feed_state: Initialization Complete\n",
+ "2024-08-28 18:38:31 [INFO] idaes.init.fs.M101.vapor_recycle_state: Initialization Complete\n",
+ "2024-08-28 18:38:31 [INFO] idaes.init.fs.M101.mixed_state: Initialization Complete\n",
+ "2024-08-28 18:38:31 [INFO] idaes.init.fs.M101: Initialization Complete: optimal - \n",
+ "WARNING: Wegstein failed to converge in 3 iterations\n",
+ "2024-08-28 18:38:31 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 1 optimal - .\n",
+ "2024-08-28 18:38:31 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 2 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 3 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 4 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_in: Initialization Step 5 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 1 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 2 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 3 optimal - .\n",
+ "2024-08-28 18:38:32 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 4 optimal - .\n",
+ "2024-08-28 18:38:33 [INFO] idaes.init.fs.H102.control_volume.properties_out: Initialization Step 5 optimal - .\n"
+ ]
+ }
+ ],
+ "source": [
+ "seq.run(m, function)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "We have now initialized the flowsheet. Let us run the flowsheet in a simulation mode to look at the results. \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 48,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "WARNING: model contains export suffix 'scaling_factor' that contains 6\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 150 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "Ipopt 3.13.2: nlp_scaling_method=gradient-based\n",
+ "tol=1e-06\n",
+ "max_iter=200\n",
+ "\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...: 1097\n",
+ "Number of nonzeros in inequality constraint Jacobian.: 0\n",
+ "Number of nonzeros in Lagrangian Hessian.............: 877\n",
+ "\n",
+ "Total number of variables............................: 363\n",
+ " variables with only lower bounds: 8\n",
+ " variables with lower and upper bounds: 155\n",
+ " variables with only upper bounds: 0\n",
+ "Total number of equality constraints.................: 363\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.82e+04 1.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
+ " 1 0.0000000e+00 8.69e+03 1.44e+03 -1.0 2.00e+04 - 9.71e-01 4.67e-01H 1\n",
+ " 2 0.0000000e+00 1.29e+03 1.56e+03 -1.0 1.60e+04 - 9.79e-01 4.90e-01h 1\n",
+ " 3 0.0000000e+00 1.18e+03 1.55e+05 -1.0 1.40e+04 - 9.90e-01 4.99e-01h 1\n",
+ " 4 0.0000000e+00 5.46e+02 2.32e+09 -1.0 8.42e+03 - 1.00e+00 9.82e-01h 1\n",
+ " 5 0.0000000e+00 5.46e+03 3.66e+10 -1.0 5.97e+02 - 1.00e+00 9.90e-01h 1\n",
+ " 6 0.0000000e+00 1.21e+03 8.01e+09 -1.0 5.75e+00 - 1.00e+00 1.00e+00h 1\n",
+ " 7 0.0000000e+00 6.41e+00 3.87e+07 -1.0 1.53e-03 - 1.00e+00 1.00e+00f 1\n",
+ " 8 0.0000000e+00 1.96e-04 9.36e+02 -1.0 7.28e-06 - 1.00e+00 1.00e+00h 1\n",
+ " 9 0.0000000e+00 2.24e-08 4.99e-01 -3.8 5.92e-08 - 1.00e+00 1.00e+00h 1\n",
+ "Cannot recompute multipliers for feasibility problem. Error in eq_mult_calculator\n",
+ "\n",
+ "Number of Iterations....: 9\n",
+ "\n",
+ " (scaled) (unscaled)\n",
+ "Objective...............: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Dual infeasibility......: 1.5042487592972509e+04 1.5042487592972509e+04\n",
+ "Constraint violation....: 2.9103830456733704e-11 2.2351741790771484e-08\n",
+ "Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Overall NLP error.......: 2.9103830456733704e-11 1.5042487592972509e+04\n",
+ "\n",
+ "\n",
+ "Number of objective function evaluations = 11\n",
+ "Number of objective gradient evaluations = 10\n",
+ "Number of equality constraint evaluations = 11\n",
+ "Number of inequality constraint evaluations = 0\n",
+ "Number of equality constraint Jacobian evaluations = 10\n",
+ "Number of inequality constraint Jacobian evaluations = 0\n",
+ "Number of Lagrangian Hessian evaluations = 9\n",
+ "Total CPU secs in IPOPT (w/o function evaluations) = 0.011\n",
+ "Total CPU secs in NLP function evaluations = 0.001\n",
+ "\n",
+ "EXIT: Optimal Solution Found.\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Create the solver object\n",
+ "solver = get_solver()\n",
+ "\n",
+ "# Solve the model\n",
+ "results = solver.solve(m, tee=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Add distillation column \n",
+ "\n",
+ "As mentioned earlier, the `SequentialDecomposition` tool currently does not support the distillation column model. Thus, we have not included the distillation column in the flowsheet. Now that we have a converged flowsheet, we will add the distillation column and simulate the entire flowsheet. \n",
+ "\n",
+ "In the following, we will\n",
+ "- Add the distillation column \n",
+ "- Connect it to the heater \n",
+ "- Add the necessary equality constraints\n",
+ "- Propagate the state variable information from the outlet of the heater to the inlet of the distillation column \n",
+ "- Fix the degrees of freedom of the distillation block (reflux ratio, boilup ratio, and condenser pressure)\n",
+ "- Scale the control volume heat variables to help convergence\n",
+ "- Initialize the distillation block.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 50,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_feed[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_liq[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_in_vap[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].material_flow_terms[Liq,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].material_flow_terms[Vap,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].material_flow_terms[Liq,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].material_flow_terms[Vap,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].enthalpy_flow_terms[Liq], enthalpy_flow_terms\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.properties_in[0.0].enthalpy_flow_terms[Vap], enthalpy_flow_terms\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].flow_mol_phase\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_comp[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_comp[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_phase_equilibrium[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_phase_equilibrium[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_P_vap[benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_out[0.0].eq_P_vap[toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].material_flow_terms[Liq,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].material_flow_terms[Vap,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].material_flow_terms[Liq,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].material_flow_terms[Vap,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].enthalpy_flow_terms[Liq], enthalpy_flow_terms\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.properties_in[0.0].enthalpy_flow_terms[Vap], enthalpy_flow_terms\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[2].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[3].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[7].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[8].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:34 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[9].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[4].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[6].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.feed_tray.e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_flow_vap_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_mole_frac_vap_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.rectification_section[1].e_mole_frac_vap_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.e_flow_reflux[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.e_mole_frac_reflux[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.condenser.control_volume.e_mole_frac_reflux[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_flow_liq_out[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_mole_frac_liq_out[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.stripping_section[10].e_mole_frac_liq_out[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.e_flow_vapor_reboil[0.0]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.e_mole_frac_vapor_reboil[0.0,benzene]\n",
+ "2024-08-28 18:38:35 [WARNING] idaes.core.util.scaling: Missing scaling factor for fs.D101.reboiler.control_volume.e_mole_frac_vapor_reboil[0.0,toluene]\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101: Begin initialization.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray: Begin initialization.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:35 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:36 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: State Released.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:37 [INFO] idaes.init.fs.D101.feed_tray.properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:38 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_out: State Released.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser.control_volume: Initialization Complete\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser: Initialization Complete, optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.condenser.control_volume.properties_in: State Released.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:39 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: State Released.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler: Initialization Complete, optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.reboiler.control_volume.properties_in: State Released.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.rectification_section[1]: Begin initialization.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:40 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:41 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: State Released.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:42 [INFO] idaes.init.fs.D101.rectification_section[2]: Begin initialization.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:43 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: State Released.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:44 [INFO] idaes.init.fs.D101.rectification_section[2]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[2]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[2]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[2].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3]: Begin initialization.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:45 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: State Released.\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:46 [INFO] idaes.init.fs.D101.rectification_section[3]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[3]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[3]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[3]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[3].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4]: Begin initialization.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:47 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:48 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: State Released.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.stripping_section[6]: Begin initialization.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:49 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:50 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: State Released.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[7]: Begin initialization.\n",
+ "2024-08-28 18:38:51 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:52 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: State Released.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:53 [INFO] idaes.init.fs.D101.stripping_section[7]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[7]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[7]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[7].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8]: Begin initialization.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:54 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: State Released.\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:55 [INFO] idaes.init.fs.D101.stripping_section[8]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[8]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[8]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[8]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[8].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9]: Begin initialization.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:56 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: State Released.\n",
+ "2024-08-28 18:38:57 [INFO] idaes.init.fs.D101.stripping_section[9].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_vap: State Released.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[9].properties_in_liq: State Released.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10]: Begin initialization.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:58 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 1 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 2 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:38:59 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 3 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 4 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Step 5 optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: State Released.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10].properties_out: Initialization Complete: optimal - Optimal Solution Found\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10]: Mass balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10]: Mass and energy balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10]: Mass, energy and pressure balance solve optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10]: Initialization complete, status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:00 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_liq: State Released.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101: Rectification section initialization status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101: Stripping section initialization status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101.rectification_section[4].properties_in_vap: State Released.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101.stripping_section[6].properties_in_liq: State Released.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101: Column section initialization status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:01 [INFO] idaes.init.fs.D101.rectification_section[1].properties_in_liq: State Released.\n",
+ "2024-08-28 18:39:02 [INFO] idaes.init.fs.D101: Column section + Condenser initialization status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:02 [INFO] idaes.init.fs.D101.stripping_section[10].properties_in_vap: State Released.\n",
+ "2024-08-28 18:39:03 [INFO] idaes.init.fs.D101: Column section + Condenser + Reboiler initialization status optimal - Optimal Solution Found.\n",
+ "2024-08-28 18:39:03 [INFO] idaes.init.fs.D101.feed_tray.properties_in_feed: State Released.\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Add distillation column to the flowsheet\n",
+ "m.fs.D101 = TrayColumn(\n",
+ " number_of_trays=10,\n",
+ " feed_tray_location=5,\n",
+ " condenser_type=CondenserType.totalCondenser,\n",
+ " condenser_temperature_spec=TemperatureSpec.atBubblePoint,\n",
+ " property_package=m.fs.BT_params,\n",
+ ")\n",
+ "\n",
+ "# Connect the outlet from the heater H102 to the distillation column\n",
+ "m.fs.s11 = Arc(source=m.fs.H102.outlet, destination=m.fs.D101.feed)\n",
+ "\n",
+ "# Add the necessary equality constraints\n",
+ "TransformationFactory(\"network.expand_arcs\").apply_to(m)\n",
+ "\n",
+ "# Propagate the state\n",
+ "propagate_state(m.fs.s11)\n",
+ "\n",
+ "# Fix the reflux ratio, boilup ratio, and the condenser pressure\n",
+ "m.fs.D101.condenser.reflux_ratio.fix(0.5)\n",
+ "m.fs.D101.reboiler.boilup_ratio.fix(0.5)\n",
+ "m.fs.D101.condenser.condenser_pressure.fix(150000)\n",
+ "\n",
+ "# set scaling factors\n",
+ "# Set scaling factors for heat duty\n",
+ "iscale.set_scaling_factor(m.fs.D101.condenser.control_volume.heat, 1e-2)\n",
+ "iscale.set_scaling_factor(m.fs.D101.reboiler.control_volume.heat, 1e-2)\n",
+ "\n",
+ "# Set the scaling factors for the remaining variables and all constraints\n",
+ "iscale.calculate_scaling_factors(m.fs.D101)\n",
+ "\n",
+ "# Initialize the distillation column\n",
+ "m.fs.D101.initialize(outlvl=idaeslog.INFO)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Adding expressions to compute capital and operating costs\n",
+ "\n",
+ "In this section, we will add a few Expressions that allow us to evaluate the performance. Expressions provide a convenient way of calculating certain values that are a function of the variables defined in the model. For more details on Expressions, please refer to: https://pyomo.readthedocs.io/en/stable/pyomo_modeling_components/Expressions.html"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 51,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Expression to compute the total cooling cost\n",
+ "m.fs.cooling_cost = Expression(\n",
+ " expr=0.25e-7 * (-m.fs.F101.heat_duty[0])\n",
+ " + 0.2e-7 * (-m.fs.D101.condenser.heat_duty[0])\n",
+ ")\n",
+ "\n",
+ "# Expression to compute the total heating cost\n",
+ "m.fs.heating_cost = Expression(\n",
+ " expr=2.2e-7 * m.fs.H101.heat_duty[0]\n",
+ " + 1.2e-7 * m.fs.H102.heat_duty[0]\n",
+ " + 1.9e-7 * m.fs.D101.reboiler.heat_duty[0]\n",
+ ")\n",
+ "\n",
+ "# Expression to compute the total operating cost\n",
+ "m.fs.operating_cost = Expression(\n",
+ " expr=(3600 * 24 * 365 * (m.fs.heating_cost + m.fs.cooling_cost))\n",
+ ")\n",
+ "\n",
+ "# Expression to compute the total capital cost\n",
+ "m.fs.capital_cost = Expression(expr=1e5 * m.fs.R101.volume[0])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Solve the entire flowsheet"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 53,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "WARNING: model contains export suffix 'scaling_factor' that contains 7\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 150 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "Ipopt 3.13.2: nlp_scaling_method=gradient-based\n",
+ "tol=1e-06\n",
+ "max_iter=200\n",
+ "\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...: 4042\n",
+ "Number of nonzeros in inequality constraint Jacobian.: 0\n",
+ "Number of nonzeros in Lagrangian Hessian.............: 2376\n",
+ "\n",
+ "Total number of variables............................: 1169\n",
+ " variables with only lower bounds: 112\n",
+ " variables with lower and upper bounds: 365\n",
+ " variables with only upper bounds: 0\n",
+ "Total number of equality constraints.................: 1169\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.83e+04 1.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
+ " 1 0.0000000e+00 8.70e+03 1.50e+03 -1.0 3.69e+04 - 9.71e-01 4.62e-01H 1\n",
+ " 2 0.0000000e+00 1.53e+03 1.56e+03 -1.0 6.75e+03 - 9.77e-01 4.89e-01h 1\n",
+ " 3 0.0000000e+00 1.37e+03 1.55e+05 -1.0 9.37e+03 - 9.90e-01 4.99e-01h 1\n",
+ " 4 0.0000000e+00 6.14e+02 2.31e+09 -1.0 6.09e+03 - 1.00e+00 9.81e-01h 1\n",
+ " 5 0.0000000e+00 5.32e+03 3.62e+10 -1.0 5.56e+02 - 1.00e+00 9.90e-01h 1\n",
+ " 6 0.0000000e+00 1.16e+03 7.80e+09 -1.0 5.36e+00 - 1.00e+00 1.00e+00h 1\n",
+ " 7 0.0000000e+00 5.96e+00 3.64e+07 -1.0 1.47e-03 - 1.00e+00 1.00e+00f 1\n",
+ " 8 0.0000000e+00 1.69e-04 8.15e+02 -1.0 6.77e-06 - 1.00e+00 1.00e+00h 1\n",
+ " 9 0.0000000e+00 7.45e-09 6.64e-03 -3.8 2.00e-07 - 1.00e+00 1.00e+00h 1\n",
+ "Cannot recompute multipliers for feasibility problem. Error in eq_mult_calculator\n",
+ "\n",
+ "Number of Iterations....: 9\n",
+ "\n",
+ " (scaled) (unscaled)\n",
+ "Objective...............: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Dual infeasibility......: 1.5042483516409773e+04 1.5042483516409773e+04\n",
+ "Constraint violation....: 2.9103830456733704e-11 7.4505805969238281e-09\n",
+ "Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Overall NLP error.......: 2.9103830456733704e-11 1.5042483516409773e+04\n",
+ "\n",
+ "\n",
+ "Number of objective function evaluations = 11\n",
+ "Number of objective gradient evaluations = 10\n",
+ "Number of equality constraint evaluations = 11\n",
+ "Number of inequality constraint evaluations = 0\n",
+ "Number of equality constraint Jacobian evaluations = 10\n",
+ "Number of inequality constraint Jacobian evaluations = 0\n",
+ "Number of Lagrangian Hessian evaluations = 9\n",
+ "Total CPU secs in IPOPT (w/o function evaluations) = 0.083\n",
+ "Total CPU secs in NLP function evaluations = 0.013\n",
+ "\n",
+ "EXIT: Optimal Solution Found.\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "{'Problem': [{'Lower bound': -inf, 'Upper bound': inf, 'Number of objectives': 1, 'Number of constraints': 1169, 'Number of variables': 1169, 'Sense': 'unknown'}], 'Solver': [{'Status': 'ok', 'Message': 'Ipopt 3.13.2\\\\x3a Optimal Solution Found', 'Termination condition': 'optimal', 'Id': 0, 'Error rc': 0, 'Time': 0.2022566795349121}], 'Solution': [OrderedDict([('number of solutions', 0), ('number of solutions displayed', 0)])]}"
+ ]
+ },
+ "execution_count": 53,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "solver.solve(m, tee=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Analyze the Results of the Square Problem\n",
+ "\n",
+ "How much is the total cost (operating cost + capital cost), operating cost, capital cost, benzene purity in the distillate from the distilation column, and conversion of toluene in the reactor?"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 55,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "total cost = $ 442301.47075252194\n",
+ "operating cost = $ 427596.73056805483\n",
+ "capital cost = $ 14704.740184467111\n",
+ "\n",
+ "Distillate flowrate = 0.16196898920633368 mol/s\n",
+ "Benzene purity = 89.4916166580088 %\n",
+ "Residue flowrate = 0.10515007120697904 mol/s\n",
+ "Toluene purity = 43.32260291055251 %\n",
+ "\n",
+ "Conversion = 75.0 %\n",
+ "\n",
+ "Overhead benzene loss in F101 = 42.161938483603194 %\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(\"total cost = $\", value(m.fs.capital_cost) + value(m.fs.operating_cost))\n",
+ "print(\"operating cost = $\", value(m.fs.operating_cost))\n",
+ "print(\"capital cost = $\", value(m.fs.capital_cost))\n",
+ "print()\n",
+ "print(\n",
+ " \"Distillate flowrate = \",\n",
+ " value(m.fs.D101.condenser.distillate.flow_mol[0]()),\n",
+ " \"mol/s\",\n",
+ ")\n",
+ "print(\n",
+ " \"Benzene purity = \",\n",
+ " 100 * value(m.fs.D101.condenser.distillate.mole_frac_comp[0, \"benzene\"]),\n",
+ " \"%\",\n",
+ ")\n",
+ "print(\"Residue flowrate = \", value(m.fs.D101.reboiler.bottoms.flow_mol[0]()), \"mol/s\")\n",
+ "print(\n",
+ " \"Toluene purity = \",\n",
+ " 100 * value(m.fs.D101.reboiler.bottoms.mole_frac_comp[0, \"toluene\"]),\n",
+ " \"%\",\n",
+ ")\n",
+ "print()\n",
+ "print(\"Conversion = \", 100 * value(m.fs.R101.conversion), \"%\")\n",
+ "print()\n",
+ "print(\n",
+ " \"Overhead benzene loss in F101 = \",\n",
+ " 100\n",
+ " * value(m.fs.F101.vap_outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"])\n",
+ " / value(m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]),\n",
+ " \"%\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Get the state of the streams entering and leaving the reactor R101"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 57,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "====================================================================================\n",
+ "Unit : fs.R101 Time: 0.0\n",
+ "------------------------------------------------------------------------------------\n",
+ " Unit Performance\n",
+ "\n",
+ " Variables: \n",
+ "\n",
+ " Key : Value : Units : Fixed : Bounds\n",
+ " Heat Duty : 0.0000 : watt : True : (None, None)\n",
+ " Volume : 0.14705 : meter ** 3 : False : (None, None)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ " Stream Table\n",
+ " Units Inlet Outlet \n",
+ " flow_mol_phase_comp ('Liq', 'benzene') mole / second 1.2993e-07 1.2993e-07\n",
+ " flow_mol_phase_comp ('Liq', 'toluene') mole / second 8.4147e-07 8.4147e-07\n",
+ " flow_mol_phase_comp ('Liq', 'methane') mole / second 1.0000e-12 1.0000e-12\n",
+ " flow_mol_phase_comp ('Liq', 'hydrogen') mole / second 1.0000e-12 1.0000e-12\n",
+ " flow_mol_phase_comp ('Vap', 'benzene') mole / second 0.11936 0.35374\n",
+ " flow_mol_phase_comp ('Vap', 'toluene') mole / second 0.31252 0.078129\n",
+ " flow_mol_phase_comp ('Vap', 'methane') mole / second 1.0377 1.2721\n",
+ " flow_mol_phase_comp ('Vap', 'hydrogen') mole / second 0.56260 0.32821\n",
+ " temperature kelvin 600.00 771.85\n",
+ " pressure pascal 3.5000e+05 3.5000e+05\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "m.fs.R101.report()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Get the state of the streams entering and leaving the reactor R101"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 58,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "====================================================================================\n",
+ "Unit : fs.F101 Time: 0.0\n",
+ "------------------------------------------------------------------------------------\n",
+ " Unit Performance\n",
+ "\n",
+ " Variables: \n",
+ "\n",
+ " Key : Value : Units : Fixed : Bounds\n",
+ " Heat Duty : -70343. : watt : False : (None, None)\n",
+ " Pressure Change : 0.0000 : pascal : True : (None, None)\n",
+ "\n",
+ "------------------------------------------------------------------------------------\n",
+ " Stream Table\n",
+ " Units Inlet Vapor Outlet Liquid Outlet\n",
+ " flow_mol_phase_comp ('Liq', 'benzene') mole / second 1.2993e-07 1.0000e-08 0.20460 \n",
+ " flow_mol_phase_comp ('Liq', 'toluene') mole / second 8.4147e-07 1.0000e-08 0.062520 \n",
+ " flow_mol_phase_comp ('Liq', 'methane') mole / second 1.0000e-12 1.0000e-08 2.6712e-07 \n",
+ " flow_mol_phase_comp ('Liq', 'hydrogen') mole / second 1.0000e-12 1.0000e-08 2.6712e-07 \n",
+ " flow_mol_phase_comp ('Vap', 'benzene') mole / second 0.35374 0.14915 1.0000e-08 \n",
+ " flow_mol_phase_comp ('Vap', 'toluene') mole / second 0.078129 0.015610 1.0000e-08 \n",
+ " flow_mol_phase_comp ('Vap', 'methane') mole / second 1.2721 1.2721 1.0000e-08 \n",
+ " flow_mol_phase_comp ('Vap', 'hydrogen') mole / second 0.32821 0.32821 1.0000e-08 \n",
+ " temperature kelvin 771.85 325.00 325.00 \n",
+ " pressure pascal 3.5000e+05 3.5000e+05 3.5000e+05 \n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "m.fs.F101.report()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Next, let's look at how much benzene we are losing with the light gases out of F101. IDAES has tools for creating stream tables based on the `Arcs` and/or `Ports` in a flowsheet. Let us create and print a simple stream table showing the stream leaving the reactor and the vapor stream from F101.\n",
+ "\n",
+ "\n",
+ "Inline Exercise:\n",
+ "How much benzene are we losing in the F101 vapor outlet stream?\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 59,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ " Units Reactor Light Gases\n",
+ "flow_mol_phase_comp ('Liq', 'benzene') mole / second 1.2993e-07 1.0000e-08 \n",
+ "flow_mol_phase_comp ('Liq', 'toluene') mole / second 8.4147e-07 1.0000e-08 \n",
+ "flow_mol_phase_comp ('Liq', 'methane') mole / second 1.0000e-12 1.0000e-08 \n",
+ "flow_mol_phase_comp ('Liq', 'hydrogen') mole / second 1.0000e-12 1.0000e-08 \n",
+ "flow_mol_phase_comp ('Vap', 'benzene') mole / second 0.35374 0.14915 \n",
+ "flow_mol_phase_comp ('Vap', 'toluene') mole / second 0.078129 0.015610 \n",
+ "flow_mol_phase_comp ('Vap', 'methane') mole / second 1.2721 1.2721 \n",
+ "flow_mol_phase_comp ('Vap', 'hydrogen') mole / second 0.32821 0.32821 \n",
+ "temperature kelvin 771.85 325.00 \n",
+ "pressure pascal 3.5000e+05 3.5000e+05 \n"
+ ]
+ }
+ ],
+ "source": [
+ "from idaes.core.util.tables import (\n",
+ " create_stream_table_dataframe,\n",
+ " stream_table_dataframe_to_string,\n",
+ ")\n",
+ "\n",
+ "st = create_stream_table_dataframe({\"Reactor\": m.fs.s05, \"Light Gases\": m.fs.s06})\n",
+ "print(stream_table_dataframe_to_string(st))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "You can query additional variables here if you like. \n",
+ "\n",
+ "Use Shift+Enter to run the cell once you have typed in your code. \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Optimization\n",
+ "\n",
+ "\n",
+ "We saw from the results above that the total operating cost for the base case was $442,297 per year. We are producing 0.162 mol/s of benzene at a purity of 89.5%. However, we are losing around 43.3% of benzene in F101 vapor outlet stream. \n",
+ "\n",
+ "Let us try to minimize this cost such that:\n",
+ "- we are producing at least 0.18 mol/s of benzene as distillate i.e. our product stream\n",
+ "- purity of benzene i.e. the mole fraction of benzene in the distillate is at least 99%\n",
+ "- restricting the benzene loss in F101 vapor outlet to less than 20%\n",
+ "\n",
+ "For this problem, our decision variables are as follows:\n",
+ "- H101 outlet temperature\n",
+ "- R101 outlet temperature\n",
+ "- F101 outlet temperature\n",
+ "- H102 outlet temperature\n",
+ "- Condenser pressure\n",
+ "- reflux ratio\n",
+ "- boilup ratio\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let us declare our objective function for this problem. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 60,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.fs.objective = Objective(expr=m.fs.operating_cost + m.fs.capital_cost)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now, we need to unfix the decision variables as we had solved a square problem (degrees of freedom = 0) until now. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 61,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.fs.H101.outlet.temperature.unfix()\n",
+ "m.fs.R101.conversion.unfix()\n",
+ "m.fs.F101.vap_outlet.temperature.unfix()\n",
+ "m.fs.D101.condenser.condenser_pressure.unfix()\n",
+ "m.fs.D101.condenser.reflux_ratio.unfix()\n",
+ "m.fs.D101.reboiler.boilup_ratio.unfix()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "Let us now unfix the remaining variable: the temperature of the outlet from H102\n",
+ "\n",
+ "Use Shift+Enter to run the cell once you have typed in your code. \n",
+ "
\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 62,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Unfix the temperature of the outlet from H102"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 63,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Unfix the temperature of the outlet from H102\n",
+ "m.fs.H102.outlet.temperature.unfix()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Next, we need to set bounds on these decision variables to values shown below:\n",
+ "\n",
+ " - H101 outlet temperature [500, 600] K\n",
+ " - R101 outlet temperature [600, 900] K\n",
+ " - F101 outlet temperature [298, 450] K\n",
+ " - H102 outlet temperature [350, 400] K\n",
+ " - D101 condenser pressure [101325, 150000] Pa\n",
+ " - D101 reflux ratio [0.1, 5]\n",
+ " - D101 boilup ratio [0.1, 5]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 64,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Set bounds on the temperature of the outlet from H101\n",
+ "m.fs.H101.outlet.temperature[0].setlb(500)\n",
+ "m.fs.H101.outlet.temperature[0].setub(600)\n",
+ "\n",
+ "# Set bounds on the temperature of the outlet from R101\n",
+ "m.fs.R101.outlet.temperature[0].setlb(600)\n",
+ "m.fs.R101.outlet.temperature[0].setub(900)\n",
+ "\n",
+ "# Set bounds on the volume of the reactor R101\n",
+ "m.fs.R101.volume[0].setlb(0)\n",
+ "\n",
+ "# Set bounds on the temperature of the vapor outlet from F101\n",
+ "m.fs.F101.vap_outlet.temperature[0].setlb(298)\n",
+ "m.fs.F101.vap_outlet.temperature[0].setub(450.0)\n",
+ "\n",
+ "# Set bounds on the temperature of the outlet from H102\n",
+ "m.fs.H102.outlet.temperature[0].setlb(350)\n",
+ "m.fs.H102.outlet.temperature[0].setub(400)\n",
+ "\n",
+ "# Set bounds on the pressure inside the condenser\n",
+ "m.fs.D101.condenser.condenser_pressure.setlb(101325)\n",
+ "m.fs.D101.condenser.condenser_pressure.setub(150000)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "Now, set the bounds for the D101 reflux ratio and boilup ratio.\n",
+ "\n",
+ "Use Shift+Enter to run the cell once you have typed in your code. \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 65,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Set bounds on the reflux ratio\n",
+ "\n",
+ "\n",
+ "# Todo: Set bounds on the boilup ratio"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 66,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Set bounds on the reflux ratio\n",
+ "m.fs.D101.condenser.reflux_ratio.setlb(0.1)\n",
+ "m.fs.D101.condenser.reflux_ratio.setub(5)\n",
+ "\n",
+ "# Todo: Set bounds on the boilup ratio\n",
+ "m.fs.D101.reboiler.boilup_ratio.setlb(0.1)\n",
+ "m.fs.D101.reboiler.boilup_ratio.setub(5)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now, the only things left to define are our constraints on overhead loss in F101, distillate flowrate and its purity. Let us first look at defining a constraint for the overhead loss in F101 where we are restricting the benzene leaving the vapor stream to less than 20 % of the benzene available in the reactor outlet. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 67,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Ensure that the overhead loss of benzene from F101 <= 20%\n",
+ "m.fs.overhead_loss = Constraint(\n",
+ " expr=m.fs.F101.vap_outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]\n",
+ " <= 0.20 * m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Inline Exercise:\n",
+ "Now, add the constraint such that we are producing at least 0.18 mol/s of benzene in the product stream which is the distillate of D101. Let us name this constraint as m.fs.product_flow. \n",
+ "\n",
+ "Use Shift+Enter to run the cell once you have typed in your code. \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 68,
+ "metadata": {
+ "tags": [
+ "exercise"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Add minimum product flow constraint"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 69,
+ "metadata": {
+ "tags": [
+ "solution"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# Todo: Add minimum product flow constraint\n",
+ "m.fs.product_flow = Constraint(expr=m.fs.D101.condenser.distillate.flow_mol[0] >= 0.18)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let us add the final constraint on product purity or the mole fraction of benzene in the distillate such that it is at least greater than 99%. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 70,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m.fs.product_purity = Constraint(\n",
+ " expr=m.fs.D101.condenser.distillate.mole_frac_comp[0, \"benzene\"] >= 0.99\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "We have now defined the optimization problem and we are now ready to solve this problem. \n",
+ "\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 71,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "WARNING: model contains export suffix 'scaling_factor' that contains 3\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "WARNING: model contains export suffix 'scaling_factor' that contains 150 keys\n",
+ "that are not Var, Constraint, Objective, or the model. Skipping.\n",
+ "Ipopt 3.13.2: nlp_scaling_method=gradient-based\n",
+ "tol=1e-06\n",
+ "max_iter=200\n",
+ "\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...: 4073\n",
+ "Number of nonzeros in inequality constraint Jacobian.: 6\n",
+ "Number of nonzeros in Lagrangian Hessian.............: 2391\n",
+ "\n",
+ "Total number of variables............................: 1176\n",
+ " variables with only lower bounds: 113\n",
+ " variables with lower and upper bounds: 372\n",
+ " variables with only upper bounds: 0\n",
+ "Total number of equality constraints.................: 1169\n",
+ "Total number of inequality constraints...............: 3\n",
+ " inequality constraints with only lower bounds: 2\n",
+ " inequality constraints with lower and upper bounds: 0\n",
+ " inequality constraints with only upper bounds: 1\n",
+ "\n",
+ "iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n",
+ " 0 4.4230147e+05 2.99e+05 9.90e+01 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
+ " 1 4.3753585e+05 2.91e+05 1.28e+02 -1.0 3.09e+06 - 3.58e-01 2.40e-02f 1\n",
+ " 2 4.3545100e+05 2.78e+05 1.55e+02 -1.0 1.78e+06 - 3.31e-01 4.74e-02h 1\n",
+ " 3 4.2822311e+05 2.20e+05 4.50e+02 -1.0 2.99e+06 - 2.95e-02 1.35e-01h 1\n",
+ " 4 4.2249096e+05 1.45e+05 1.43e+03 -1.0 7.01e+06 - 5.14e-01 2.03e-01h 1\n",
+ " 5 4.2194364e+05 8.17e+04 1.70e+04 -1.0 6.06e+06 - 5.97e-01 4.28e-01h 1\n",
+ " 6 4.2602765e+05 4.55e+04 1.10e+06 -1.0 4.32e+06 - 9.26e-01 5.07e-01h 1\n",
+ " 7 4.3776643e+05 2.03e+04 6.44e+09 -1.0 2.42e+06 - 9.90e-01 9.47e-01h 1\n",
+ " 8 4.3846260e+05 1.92e+04 6.05e+09 -1.0 4.42e+05 - 5.40e-01 5.74e-02h 1\n",
+ " 9 4.4529853e+05 4.05e+04 4.66e+10 -1.0 2.47e+05 - 9.96e-01 9.90e-01h 1\n",
+ "iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n",
+ " 10 4.4906283e+05 9.76e+03 1.10e+10 -1.0 1.12e+03 -4.0 1.26e-01 7.45e-01h 1\n",
+ " 11 4.5079086e+05 1.19e+03 1.54e+09 -1.0 5.63e+02 -4.5 3.77e-01 1.00e+00h 1\n",
+ " 12 4.5024224e+05 2.66e+00 3.67e+06 -1.0 6.61e+01 -5.0 1.00e+00 1.00e+00f 1\n",
+ " 13 4.4946170e+05 5.64e-01 9.29e+05 -1.0 1.81e+02 -5.4 1.00e+00 7.88e-01f 1\n",
+ " 14 4.4916780e+05 8.48e+00 1.62e+05 -1.0 2.83e+02 -5.9 1.00e+00 1.00e+00f 1\n",
+ " 15 4.4899127e+05 4.83e+00 9.07e+04 -1.0 1.01e+02 -6.4 1.00e+00 4.40e-01f 2\n",
+ " 16 4.4886718e+05 7.00e-01 4.61e+02 -1.0 2.35e+02 -6.9 1.00e+00 1.00e+00f 1\n",
+ " 17 4.4800159e+05 1.39e+02 4.52e+06 -3.8 1.17e+03 -7.3 9.79e-01 9.37e-01f 1\n",
+ " 18 4.4672196e+05 9.59e+02 1.22e+06 -3.8 4.55e+03 -7.8 1.00e+00 9.43e-01f 1\n",
+ " 19 4.4401667e+05 7.75e+03 1.55e+05 -3.8 1.08e+04 -8.3 1.00e+00 1.00e+00f 1\n",
+ "iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n",
+ " 20 4.4185035e+05 1.91e+04 1.36e+04 -3.8 1.33e+04 -8.8 1.00e+00 1.00e+00h 1\n",
+ " 21 4.4241001e+05 3.52e+03 5.96e+03 -3.8 2.94e+03 -9.2 1.00e+00 1.00e+00h 1\n",
+ " 22 4.4185237e+05 7.82e+00 2.91e+02 -3.8 7.13e+03 -9.7 2.39e-01 1.00e+00h 1\n",
+ " 23 4.4124091e+05 1.53e+01 3.11e+02 -3.8 4.82e+04 -10.2 8.59e-01 1.36e-01f 1\n",
+ " 24 4.4137379e+05 1.80e+00 2.91e+02 -3.8 1.41e+04 - 1.95e-01 1.00e+00h 1\n",
+ " 25 4.3862833e+05 1.70e+03 9.48e+04 -3.8 1.57e+07 - 1.29e-03 9.10e-02f 1\n",
+ " 26 4.3883308e+05 1.49e+03 8.46e+04 -3.8 1.02e+06 - 1.00e+00 1.35e-01h 1\n",
+ " 27 4.3885472e+05 2.18e+01 3.40e+03 -3.8 1.38e+05 - 1.00e+00 1.00e+00h 1\n",
+ " 28 4.3884160e+05 5.90e-02 6.38e+01 -3.8 8.66e+03 - 1.00e+00 1.00e+00h 1\n",
+ " 29 4.3884157e+05 6.48e-07 4.63e-04 -3.8 2.89e+01 - 1.00e+00 1.00e+00h 1\n",
+ "iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls\n",
+ " 30 4.3883990e+05 3.57e-01 2.38e+03 -5.7 8.19e+02 - 1.00e+00 1.00e+00f 1\n",
+ " 31 4.3883992e+05 3.50e-07 7.79e-06 -5.7 3.55e-01 - 1.00e+00 1.00e+00h 1\n",
+ " 32 4.3883990e+05 5.47e-05 3.63e-01 -8.0 1.01e+01 - 1.00e+00 1.00e+00h 1\n",
+ " 33 4.3883990e+05 2.24e-08 1.46e-07 -8.0 5.42e-05 - 1.00e+00 1.00e+00h 1\n",
+ "\n",
+ "Number of Iterations....: 33\n",
+ "\n",
+ " (scaled) (unscaled)\n",
+ "Objective...............: 4.3883989842628603e+02 4.3883989842628600e+05\n",
+ "Dual infeasibility......: 1.4600704448671754e-07 1.4600704448671753e-04\n",
+ "Constraint violation....: 2.9103830456733704e-11 2.2351741790771484e-08\n",
+ "Complementarity.........: 9.0909948039799681e-09 9.0909948039799686e-06\n",
+ "Overall NLP error.......: 9.0909948039799681e-09 1.4600704448671753e-04\n",
+ "\n",
+ "\n",
+ "Number of objective function evaluations = 35\n",
+ "Number of objective gradient evaluations = 34\n",
+ "Number of equality constraint evaluations = 35\n",
+ "Number of inequality constraint evaluations = 35\n",
+ "Number of equality constraint Jacobian evaluations = 34\n",
+ "Number of inequality constraint Jacobian evaluations = 34\n",
+ "Number of Lagrangian Hessian evaluations = 33\n",
+ "Total CPU secs in IPOPT (w/o function evaluations) = 0.164\n",
+ "Total CPU secs in NLP function evaluations = 0.020\n",
+ "\n",
+ "EXIT: Optimal Solution Found.\n"
+ ]
+ }
+ ],
+ "source": [
+ "results = solver.solve(m, tee=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Optimization Results\n",
+ "\n",
+ "Display the results and product specifications"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 73,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "total cost = $ 438839.898426286\n",
+ "operating cost = $ 408883.5314830889\n",
+ "capital cost = $ 29956.3669431971\n",
+ "\n",
+ "Distillate flowrate = 0.1799999900263989 mol/s\n",
+ "Benzene purity = 98.99999900049086 %\n",
+ "Residue flowrate = 0.1085161642426372 mol/s\n",
+ "Toluene purity = 15.676178086213548 %\n",
+ "\n",
+ "Conversion = 93.38705916369427 %\n",
+ "\n",
+ "Overhead benzene loss in F101 = 17.34061793115618 %\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(\"total cost = $\", value(m.fs.capital_cost) + value(m.fs.operating_cost))\n",
+ "print(\"operating cost = $\", value(m.fs.operating_cost))\n",
+ "print(\"capital cost = $\", value(m.fs.capital_cost))\n",
+ "print()\n",
+ "print(\n",
+ " \"Distillate flowrate = \",\n",
+ " value(m.fs.D101.condenser.distillate.flow_mol[0]()),\n",
+ " \"mol/s\",\n",
+ ")\n",
+ "print(\n",
+ " \"Benzene purity = \",\n",
+ " 100 * value(m.fs.D101.condenser.distillate.mole_frac_comp[0, \"benzene\"]),\n",
+ " \"%\",\n",
+ ")\n",
+ "print(\"Residue flowrate = \", value(m.fs.D101.reboiler.bottoms.flow_mol[0]()), \"mol/s\")\n",
+ "print(\n",
+ " \"Toluene purity = \",\n",
+ " 100 * value(m.fs.D101.reboiler.bottoms.mole_frac_comp[0, \"toluene\"]),\n",
+ " \"%\",\n",
+ ")\n",
+ "print()\n",
+ "print(\"Conversion = \", 100 * value(m.fs.R101.conversion), \"%\")\n",
+ "print()\n",
+ "print(\n",
+ " \"Overhead benzene loss in F101 = \",\n",
+ " 100\n",
+ " * value(m.fs.F101.vap_outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"])\n",
+ " / value(m.fs.R101.outlet.flow_mol_phase_comp[0, \"Vap\", \"benzene\"]),\n",
+ " \"%\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Display optimal values for the decision variables"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 75,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Optimal Values\n",
+ "\n",
+ "H101 outlet temperature = 568.923204295196 K\n",
+ "\n",
+ "R101 outlet temperature = 790.3655425698853 K\n",
+ "\n",
+ "F101 outlet temperature = 298.0 K\n",
+ "\n",
+ "H102 outlet temperature = 368.7414339952852 K\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(\"Optimal Values\")\n",
+ "print()\n",
+ "\n",
+ "print(\"H101 outlet temperature = \", value(m.fs.H101.outlet.temperature[0]), \"K\")\n",
+ "\n",
+ "print()\n",
+ "print(\"R101 outlet temperature = \", value(m.fs.R101.outlet.temperature[0]), \"K\")\n",
+ "\n",
+ "print()\n",
+ "print(\"F101 outlet temperature = \", value(m.fs.F101.vap_outlet.temperature[0]), \"K\")\n",
+ "\n",
+ "print()\n",
+ "print(\"H102 outlet temperature = \", value(m.fs.H102.outlet.temperature[0]), \"K\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Key Takeaways\n",
+ "\n",
+ "Observe that the optimization was able to reduce the yearly operating cost from \\\\$427,593 to \\\\$408,342 (~4.5%). However, the amortized capital cost more than doubled from \\\\$14,704 to \\\\$29,927 due to the need to increase the conversion in the reactor (from 75% to 93%) to meet the production and purity constraints. \n",
+ "\n",
+ "Further, observe that the product flow rate and product purity are at their minimum values (0.18 mol/s and 99%, respectively). This is expected as increasing recovery would require more energy and cost to purify the product.\n",
+ "\n",
+ "\n",
+ "Finally, observe that the operating temperature of the flash (F101) is almost at its lower bound. This helps in minimizing the amount of benzene in the vapor stream leaving the flash."
+ ]
+ }
+ ],
+ "metadata": {
+ "celltoolbar": "Tags",
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
},
- "nbformat": 4,
- "nbformat_minor": 3
+ "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.10.13"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 3
}
\ No newline at end of file
diff --git a/idaes_examples/notebooks/docs/flowsheets/hda_flowsheets_for_costing_notebook.py b/idaes_examples/notebooks/docs/flowsheets/hda_flowsheets_for_costing_notebook.py
index 83c8d23e..e365fcd9 100644
--- a/idaes_examples/notebooks/docs/flowsheets/hda_flowsheets_for_costing_notebook.py
+++ b/idaes_examples/notebooks/docs/flowsheets/hda_flowsheets_for_costing_notebook.py
@@ -15,36 +15,38 @@
"""
# Import Pyomo libraries
-from pyomo.environ import (Constraint,
- Var,
- Param,
- Expression,
- ConcreteModel,
- SolverFactory,
- TransformationFactory,
- units as pyunits,
- TerminationCondition)
+from pyomo.environ import (
+ Constraint,
+ Var,
+ Param,
+ Expression,
+ ConcreteModel,
+ SolverFactory,
+ TransformationFactory,
+ units as pyunits,
+ TerminationCondition,
+)
from pyomo.network import Arc, SequentialDecomposition
# Import IDAES core libraries
from idaes.core import FlowsheetBlock
# Import IDAES generic unit models
-from idaes.models.unit_models import (PressureChanger,
- Mixer,
- Separator as Splitter,
- Heater,
- StoichiometricReactor,
- CSTR,
- Flash,
- Translator)
-from idaes.models.unit_models.pressure_changer import \
- ThermodynamicAssumption
+from idaes.models.unit_models import (
+ PressureChanger,
+ Mixer,
+ Separator as Splitter,
+ Heater,
+ StoichiometricReactor,
+ CSTR,
+ Flash,
+ Translator,
+)
+from idaes.models.unit_models.pressure_changer import ThermodynamicAssumption
# Import IDAES distillation unit models
from idaes.models_extra.column_models.tray_column import TrayColumn
-from idaes.models_extra.column_models.condenser import \
- CondenserType, TemperatureSpec
+from idaes.models_extra.column_models.condenser import CondenserType, TemperatureSpec
# Import IDAES core utilities
from idaes.core.util.initialization import propagate_state
@@ -52,6 +54,7 @@
import idaes.core.util.scaling as iscale
from pyomo.util.check_units import assert_units_consistent
from idaes.core.util.model_statistics import degrees_of_freedom
+from idaes.core.util.exceptions import InitializationError
# Import costing methods - classes, heaters, vessels, compressors, columns
from idaes.models.costing.SSLW import (
@@ -84,7 +87,7 @@ def hda_with_flash(tee=True):
from idaes_examples.mod.hda import hda_reaction as reaction_props
# build flowsheet
- print('Building flowsheet...')
+ print("Building flowsheet...")
print()
m = ConcreteModel()
@@ -92,103 +95,151 @@ def hda_with_flash(tee=True):
m.fs.thermo_params = thermo_props.HDAParameterBlock()
m.fs.reaction_params = reaction_props.HDAReactionParameterBlock(
- property_package=m.fs.thermo_params)
-
- m.fs.M101 = Mixer(property_package=m.fs.thermo_params,
- inlet_list=["toluene_feed", "hydrogen_feed",
- "vapor_recycle"])
- m.fs.H101 = Heater(property_package=m.fs.thermo_params,
- has_pressure_change=False,
- has_phase_equilibrium=True)
+ property_package=m.fs.thermo_params
+ )
+
+ m.fs.M101 = Mixer(
+ property_package=m.fs.thermo_params,
+ inlet_list=["toluene_feed", "hydrogen_feed", "vapor_recycle"],
+ )
+ m.fs.H101 = Heater(
+ property_package=m.fs.thermo_params,
+ has_pressure_change=False,
+ has_phase_equilibrium=True,
+ )
m.fs.R101 = StoichiometricReactor(
- property_package=m.fs.thermo_params,
- reaction_package=m.fs.reaction_params,
- has_heat_of_reaction=True,
- has_heat_transfer=True,
- has_pressure_change=False)
- m.fs.F101 = Flash(property_package=m.fs.thermo_params,
- has_heat_transfer=True,
- has_pressure_change=True)
- m.fs.S101 = Splitter(property_package=m.fs.thermo_params,
- ideal_separation=False,
- outlet_list=["purge", "recycle"])
+ property_package=m.fs.thermo_params,
+ reaction_package=m.fs.reaction_params,
+ has_heat_of_reaction=True,
+ has_heat_transfer=True,
+ has_pressure_change=False,
+ )
+ m.fs.F101 = Flash(
+ property_package=m.fs.thermo_params,
+ has_heat_transfer=True,
+ has_pressure_change=True,
+ )
+ m.fs.S101 = Splitter(
+ property_package=m.fs.thermo_params,
+ ideal_separation=False,
+ outlet_list=["purge", "recycle"],
+ )
m.fs.C101 = PressureChanger(
property_package=m.fs.thermo_params,
compressor=True,
- thermodynamic_assumption=ThermodynamicAssumption.isothermal)
- m.fs.F102 = Flash(property_package=m.fs.thermo_params,
- has_heat_transfer=True,
- has_pressure_change=True)
+ thermodynamic_assumption=ThermodynamicAssumption.isothermal,
+ )
+ m.fs.F102 = Flash(
+ property_package=m.fs.thermo_params,
+ has_heat_transfer=True,
+ has_pressure_change=True,
+ )
m.fs.s03 = Arc(source=m.fs.M101.outlet, destination=m.fs.H101.inlet)
m.fs.s04 = Arc(source=m.fs.H101.outlet, destination=m.fs.R101.inlet)
m.fs.s05 = Arc(source=m.fs.R101.outlet, destination=m.fs.F101.inlet)
m.fs.s06 = Arc(source=m.fs.F101.vap_outlet, destination=m.fs.S101.inlet)
m.fs.s08 = Arc(source=m.fs.S101.recycle, destination=m.fs.C101.inlet)
- m.fs.s09 = Arc(source=m.fs.C101.outlet,
- destination=m.fs.M101.vapor_recycle)
+ m.fs.s09 = Arc(source=m.fs.C101.outlet, destination=m.fs.M101.vapor_recycle)
m.fs.s10 = Arc(source=m.fs.F101.liq_outlet, destination=m.fs.F102.inlet)
TransformationFactory("network.expand_arcs").apply_to(m)
# set inputs
- print('Setting inputs...')
+ print("Setting inputs...")
print()
- m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Vap", "benzene"].fix(1e-5*pyunits.mol/pyunits.s)
- m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Vap", "toluene"].fix(1e-5*pyunits.mol/pyunits.s)
- m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Vap", "hydrogen"].fix(1e-5*pyunits.mol/pyunits.s)
- m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Vap", "methane"].fix(1e-5*pyunits.mol/pyunits.s)
- m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Liq", "benzene"].fix(1e-5*pyunits.mol/pyunits.s)
- m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Liq", "toluene"].fix(0.30*pyunits.mol/pyunits.s)
- m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Liq", "hydrogen"].fix(1e-5*pyunits.mol/pyunits.s)
- m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Liq", "methane"].fix(1e-5*pyunits.mol/pyunits.s)
- m.fs.M101.toluene_feed.temperature.fix(303.2*pyunits.K)
- m.fs.M101.toluene_feed.pressure.fix(350000*pyunits.Pa)
-
- m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Vap", "benzene"].fix(1e-5*pyunits.mol/pyunits.s)
- m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Vap", "toluene"].fix(1e-5*pyunits.mol/pyunits.s)
- m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Vap", "hydrogen"].fix(0.30*pyunits.mol/pyunits.s)
- m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Vap", "methane"].fix(0.02*pyunits.mol/pyunits.s)
- m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Liq", "benzene"].fix(1e-5*pyunits.mol/pyunits.s)
- m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Liq", "toluene"].fix(1e-5*pyunits.mol/pyunits.s)
- m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Liq", "hydrogen"].fix(1e-5*pyunits.mol/pyunits.s)
- m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Liq", "methane"].fix(1e-5*pyunits.mol/pyunits.s)
- m.fs.M101.hydrogen_feed.temperature.fix(303.2*pyunits.K)
- m.fs.M101.hydrogen_feed.pressure.fix(350000*pyunits.Pa)
-
- m.fs.H101.outlet.temperature.fix(600*pyunits.K)
+ m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Vap", "benzene"].fix(
+ 1e-5 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Vap", "toluene"].fix(
+ 1e-5 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Vap", "hydrogen"].fix(
+ 1e-5 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Vap", "methane"].fix(
+ 1e-5 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Liq", "benzene"].fix(
+ 1e-5 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Liq", "toluene"].fix(
+ 0.30 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Liq", "hydrogen"].fix(
+ 1e-5 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Liq", "methane"].fix(
+ 1e-5 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.toluene_feed.temperature.fix(303.2 * pyunits.K)
+ m.fs.M101.toluene_feed.pressure.fix(350000 * pyunits.Pa)
+
+ m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Vap", "benzene"].fix(
+ 1e-5 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Vap", "toluene"].fix(
+ 1e-5 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Vap", "hydrogen"].fix(
+ 0.30 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Vap", "methane"].fix(
+ 0.02 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Liq", "benzene"].fix(
+ 1e-5 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Liq", "toluene"].fix(
+ 1e-5 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Liq", "hydrogen"].fix(
+ 1e-5 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Liq", "methane"].fix(
+ 1e-5 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.hydrogen_feed.temperature.fix(303.2 * pyunits.K)
+ m.fs.M101.hydrogen_feed.pressure.fix(350000 * pyunits.Pa)
+
+ m.fs.H101.outlet.temperature.fix(600 * pyunits.K)
m.fs.R101.conversion = Var(initialize=0.75, bounds=(0, 1))
m.fs.R101.conv_constraint = Constraint(
- expr=m.fs.R101.conversion*m.fs.R101.inlet.
- flow_mol_phase_comp[0, "Vap", "toluene"] ==
- (m.fs.R101.inlet.flow_mol_phase_comp[0, "Vap", "toluene"] -
- m.fs.R101.outlet.flow_mol_phase_comp[0, "Vap", "toluene"]))
+ expr=m.fs.R101.conversion
+ * m.fs.R101.inlet.flow_mol_phase_comp[0, "Vap", "toluene"]
+ == (
+ m.fs.R101.inlet.flow_mol_phase_comp[0, "Vap", "toluene"]
+ - m.fs.R101.outlet.flow_mol_phase_comp[0, "Vap", "toluene"]
+ )
+ )
- m.fs.R101.conversion.fix(0.75*pyunits.dimensionless)
- m.fs.R101.heat_duty.fix(0*pyunits.W)
+ m.fs.R101.conversion.fix(0.75 * pyunits.dimensionless)
+ m.fs.R101.heat_duty.fix(0 * pyunits.W)
- m.fs.F101.vap_outlet.temperature.fix(325.0*pyunits.K)
- m.fs.F101.deltaP.fix(0*pyunits.Pa)
+ m.fs.F101.vap_outlet.temperature.fix(325.0 * pyunits.K)
+ m.fs.F101.deltaP.fix(0 * pyunits.Pa)
- m.fs.F102.vap_outlet.temperature.fix(375*pyunits.K)
- m.fs.F102.deltaP.fix(-200000*pyunits.Pa)
+ m.fs.F102.vap_outlet.temperature.fix(375 * pyunits.K)
+ m.fs.F102.deltaP.fix(-200000 * pyunits.Pa)
m.fs.S101.split_fraction[0, "purge"].fix(0.2)
- m.fs.C101.outlet.pressure.fix(350000*pyunits.Pa)
+ m.fs.C101.outlet.pressure.fix(350000 * pyunits.Pa)
# initialize flowsheet
- print('Initializing flowsheet...')
+ print("Initializing flowsheet...")
print()
seq = SequentialDecomposition()
seq.options.select_tear_method = "heuristic"
seq.options.tear_method = "Wegstein"
seq.options.iterLim = 3
- print('Limiting Wegstein tear to 3 iterations to obtain initial solution,'
- ' if not converged IPOPT will pick up and continue.')
+ print(
+ "Limiting Wegstein tear to 3 iterations to obtain initial solution,"
+ " if not converged IPOPT will pick up and continue."
+ )
print()
G = seq.create_graph(m)
@@ -202,35 +253,42 @@ def hda_with_flash(tee=True):
tear_guesses = {
"flow_mol_phase_comp": {
- (0, "Vap", "benzene"): 1e-5,
- (0, "Vap", "toluene"): 1e-5,
- (0, "Vap", "hydrogen"): 0.30,
- (0, "Vap", "methane"): 0.02,
- (0, "Liq", "benzene"): 1e-5,
- (0, "Liq", "toluene"): 0.30,
- (0, "Liq", "hydrogen"): 1e-5,
- (0, "Liq", "methane"): 1e-5},
+ (0, "Vap", "benzene"): 1e-5,
+ (0, "Vap", "toluene"): 1e-5,
+ (0, "Vap", "hydrogen"): 0.30,
+ (0, "Vap", "methane"): 0.02,
+ (0, "Liq", "benzene"): 1e-5,
+ (0, "Liq", "toluene"): 0.30,
+ (0, "Liq", "hydrogen"): 1e-5,
+ (0, "Liq", "methane"): 1e-5,
+ },
"temperature": {0: 303},
- "pressure": {0: 350000}}
+ "pressure": {0: 350000},
+ }
seq.set_guesses_for(m.fs.H101.inlet, tear_guesses)
def function(unit):
- unit.initialize(outlvl=outlvl)
+ try:
+ initializer = unit.default_initializer()
+ initializer.initialize(unit, output_level=idaeslog.INFO)
+ except InitializationError:
+ solver = get_solver()
+ solver.solve(unit)
seq.run(m, function)
# solve model
- print('Solving flowsheet...')
+ print("Solving flowsheet...")
print()
- solver = SolverFactory('ipopt')
- solver.options = {'tol': 1e-6, 'max_iter': 5000}
+ solver = SolverFactory("ipopt")
+ solver.options = {"tol": 1e-6, "max_iter": 5000}
results = solver.solve(m, tee=tee)
assert results.solver.termination_condition == TerminationCondition.optimal
assert_units_consistent(m)
- print('Complete.')
+ print("Complete.")
print()
return m
@@ -244,12 +302,13 @@ def hda_with_distillation(tee=True):
# Import thermodynamic and reaction property packages
from idaes_examples.mod.hda import hda_reaction as reaction_props
- from idaes.models.properties.activity_coeff_models.\
- BTX_activity_coeff_VLE import BTXParameterBlock
+ from idaes.models.properties.activity_coeff_models.BTX_activity_coeff_VLE import (
+ BTXParameterBlock,
+ )
from idaes_examples.mod.hda.hda_ideal_VLE import HDAParameterBlock
# build flowsheet
- print('Building flowsheet...')
+ print("Building flowsheet...")
print()
m = ConcreteModel()
@@ -257,129 +316,176 @@ def hda_with_distillation(tee=True):
m.fs.BTHM_params = HDAParameterBlock()
m.fs.BT_params = BTXParameterBlock(
- valid_phase=('Liq', 'Vap'),
- activity_coeff_model="Ideal"
- )
+ valid_phase=("Liq", "Vap"), activity_coeff_model="Ideal"
+ )
m.fs.reaction_params = reaction_props.HDAReactionParameterBlock(
- property_package=m.fs.BTHM_params)
-
- m.fs.M101 = Mixer(property_package=m.fs.BTHM_params,
- inlet_list=["toluene_feed", "hydrogen_feed",
- "vapor_recycle"])
- m.fs.H101 = Heater(property_package=m.fs.BTHM_params,
- has_phase_equilibrium=True)
+ property_package=m.fs.BTHM_params
+ )
+
+ m.fs.M101 = Mixer(
+ property_package=m.fs.BTHM_params,
+ inlet_list=["toluene_feed", "hydrogen_feed", "vapor_recycle"],
+ )
+ m.fs.H101 = Heater(property_package=m.fs.BTHM_params, has_phase_equilibrium=True)
m.fs.R101 = CSTR(
- property_package=m.fs.BTHM_params,
- reaction_package=m.fs.reaction_params,
- has_heat_of_reaction=True,
- has_heat_transfer=True)
- m.fs.F101 = Flash(property_package=m.fs.BTHM_params,
- has_heat_transfer=True,
- has_pressure_change=True)
- m.fs.S101 = Splitter(property_package=m.fs.BTHM_params,
- outlet_list=["purge", "recycle"])
+ property_package=m.fs.BTHM_params,
+ reaction_package=m.fs.reaction_params,
+ has_heat_of_reaction=True,
+ has_heat_transfer=True,
+ )
+ m.fs.F101 = Flash(
+ property_package=m.fs.BTHM_params,
+ has_heat_transfer=True,
+ has_pressure_change=True,
+ )
+ m.fs.S101 = Splitter(
+ property_package=m.fs.BTHM_params, outlet_list=["purge", "recycle"]
+ )
m.fs.C101 = PressureChanger(
- property_package=m.fs.BTHM_params,
- compressor=True,
- thermodynamic_assumption=ThermodynamicAssumption.isothermal)
+ property_package=m.fs.BTHM_params,
+ compressor=True,
+ thermodynamic_assumption=ThermodynamicAssumption.isothermal,
+ )
m.fs.translator = Translator(
- inlet_property_package=m.fs.BTHM_params,
- outlet_property_package=m.fs.BT_params
- )
+ inlet_property_package=m.fs.BTHM_params, outlet_property_package=m.fs.BT_params
+ )
# Add constraint: Total flow = benzene flow + toluene flow (molar)
m.fs.translator.eq_total_flow = Constraint(
- expr=m.fs.translator.outlet.flow_mol[0] ==
- m.fs.translator.inlet.flow_mol_phase_comp[0, "Liq", "benzene"] +
- m.fs.translator.inlet.flow_mol_phase_comp[0, "Liq", "toluene"])
+ expr=m.fs.translator.outlet.flow_mol[0]
+ == m.fs.translator.inlet.flow_mol_phase_comp[0, "Liq", "benzene"]
+ + m.fs.translator.inlet.flow_mol_phase_comp[0, "Liq", "toluene"]
+ )
# Add constraint: Outlet temperature = Inlet temperature
m.fs.translator.eq_temperature = Constraint(
- expr=m.fs.translator.outlet.temperature[0] ==
- m.fs.translator.inlet.temperature[0])
+ expr=m.fs.translator.outlet.temperature[0]
+ == m.fs.translator.inlet.temperature[0]
+ )
m.fs.translator.eq_pressure = Constraint(
- expr=m.fs.translator.outlet.pressure[0] ==
- m.fs.translator.inlet.pressure[0])
+ expr=m.fs.translator.outlet.pressure[0] == m.fs.translator.inlet.pressure[0]
+ )
# Add constraint: Benzene mole fraction definition
m.fs.translator.eq_mole_frac_benzene = Constraint(
- expr=m.fs.translator.outlet.mole_frac_comp[0, "benzene"] ==
- m.fs.translator.inlet.flow_mol_phase_comp[0, "Liq", "benzene"] /
- (m.fs.translator.inlet.flow_mol_phase_comp[0, "Liq", "benzene"] +
- m.fs.translator.inlet.flow_mol_phase_comp[0, "Liq", "toluene"]))
+ expr=m.fs.translator.outlet.mole_frac_comp[0, "benzene"]
+ == m.fs.translator.inlet.flow_mol_phase_comp[0, "Liq", "benzene"]
+ / (
+ m.fs.translator.inlet.flow_mol_phase_comp[0, "Liq", "benzene"]
+ + m.fs.translator.inlet.flow_mol_phase_comp[0, "Liq", "toluene"]
+ )
+ )
# Add constraint: Toluene mole fraction definition
m.fs.translator.eq_mole_frac_toluene = Constraint(
- expr=m.fs.translator.outlet.mole_frac_comp[0, "toluene"] ==
- m.fs.translator.inlet.flow_mol_phase_comp[0, "Liq", "toluene"] /
- (m.fs.translator.inlet.flow_mol_phase_comp[0, "Liq", "benzene"] +
- m.fs.translator.inlet.flow_mol_phase_comp[0, "Liq", "toluene"]))
+ expr=m.fs.translator.outlet.mole_frac_comp[0, "toluene"]
+ == m.fs.translator.inlet.flow_mol_phase_comp[0, "Liq", "toluene"]
+ / (
+ m.fs.translator.inlet.flow_mol_phase_comp[0, "Liq", "benzene"]
+ + m.fs.translator.inlet.flow_mol_phase_comp[0, "Liq", "toluene"]
+ )
+ )
- m.fs.H102 = Heater(property_package=m.fs.BT_params,
- has_pressure_change=True,
- has_phase_equilibrium=True)
+ m.fs.H102 = Heater(
+ property_package=m.fs.BT_params,
+ has_pressure_change=True,
+ has_phase_equilibrium=True,
+ )
m.fs.s03 = Arc(source=m.fs.M101.outlet, destination=m.fs.H101.inlet)
m.fs.s04 = Arc(source=m.fs.H101.outlet, destination=m.fs.R101.inlet)
m.fs.s05 = Arc(source=m.fs.R101.outlet, destination=m.fs.F101.inlet)
m.fs.s06 = Arc(source=m.fs.F101.vap_outlet, destination=m.fs.S101.inlet)
m.fs.s08 = Arc(source=m.fs.S101.recycle, destination=m.fs.C101.inlet)
- m.fs.s09 = Arc(source=m.fs.C101.outlet,
- destination=m.fs.M101.vapor_recycle)
- m.fs.s10a = Arc(source=m.fs.F101.liq_outlet,
- destination=m.fs.translator.inlet)
+ m.fs.s09 = Arc(source=m.fs.C101.outlet, destination=m.fs.M101.vapor_recycle)
+ m.fs.s10a = Arc(source=m.fs.F101.liq_outlet, destination=m.fs.translator.inlet)
m.fs.s10b = Arc(source=m.fs.translator.outlet, destination=m.fs.H102.inlet)
TransformationFactory("network.expand_arcs").apply_to(m)
# set inputs
- print('Setting inputs...')
+ print("Setting inputs...")
print()
- m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Vap", "benzene"].fix(1e-5*pyunits.mol/pyunits.s)
- m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Vap", "toluene"].fix(1e-5*pyunits.mol/pyunits.s)
- m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Vap", "hydrogen"].fix(1e-5*pyunits.mol/pyunits.s)
- m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Vap", "methane"].fix(1e-5*pyunits.mol/pyunits.s)
- m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Liq", "benzene"].fix(1e-5*pyunits.mol/pyunits.s)
- m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Liq", "toluene"].fix(0.30*pyunits.mol/pyunits.s)
- m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Liq", "hydrogen"].fix(1e-5*pyunits.mol/pyunits.s)
- m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Liq", "methane"].fix(1e-5*pyunits.mol/pyunits.s)
- m.fs.M101.toluene_feed.temperature.fix(303.2*pyunits.K)
- m.fs.M101.toluene_feed.pressure.fix(350000*pyunits.Pa)
-
- m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Vap", "benzene"].fix(1e-5*pyunits.mol/pyunits.s)
- m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Vap", "toluene"].fix(1e-5*pyunits.mol/pyunits.s)
- m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Vap", "hydrogen"].fix(0.30*pyunits.mol/pyunits.s)
- m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Vap", "methane"].fix(0.02*pyunits.mol/pyunits.s)
- m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Liq", "benzene"].fix(1e-5*pyunits.mol/pyunits.s)
- m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Liq", "toluene"].fix(1e-5*pyunits.mol/pyunits.s)
- m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Liq", "hydrogen"].fix(1e-5*pyunits.mol/pyunits.s)
- m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Liq", "methane"].fix(1e-5*pyunits.mol/pyunits.s)
- m.fs.M101.hydrogen_feed.temperature.fix(303.2*pyunits.K)
- m.fs.M101.hydrogen_feed.pressure.fix(350000*pyunits.Pa)
-
- m.fs.H101.outlet.temperature.fix(600*pyunits.K)
+ m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Vap", "benzene"].fix(
+ 1e-5 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Vap", "toluene"].fix(
+ 1e-5 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Vap", "hydrogen"].fix(
+ 1e-5 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Vap", "methane"].fix(
+ 1e-5 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Liq", "benzene"].fix(
+ 1e-5 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Liq", "toluene"].fix(
+ 0.30 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Liq", "hydrogen"].fix(
+ 1e-5 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.toluene_feed.flow_mol_phase_comp[0, "Liq", "methane"].fix(
+ 1e-5 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.toluene_feed.temperature.fix(303.2 * pyunits.K)
+ m.fs.M101.toluene_feed.pressure.fix(350000 * pyunits.Pa)
+
+ m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Vap", "benzene"].fix(
+ 1e-5 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Vap", "toluene"].fix(
+ 1e-5 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Vap", "hydrogen"].fix(
+ 0.30 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Vap", "methane"].fix(
+ 0.02 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Liq", "benzene"].fix(
+ 1e-5 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Liq", "toluene"].fix(
+ 1e-5 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Liq", "hydrogen"].fix(
+ 1e-5 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.hydrogen_feed.flow_mol_phase_comp[0, "Liq", "methane"].fix(
+ 1e-5 * pyunits.mol / pyunits.s
+ )
+ m.fs.M101.hydrogen_feed.temperature.fix(303.2 * pyunits.K)
+ m.fs.M101.hydrogen_feed.pressure.fix(350000 * pyunits.Pa)
+
+ m.fs.H101.outlet.temperature.fix(600 * pyunits.K)
m.fs.R101.conversion = Var(initialize=0.75, bounds=(0, 1))
m.fs.R101.conv_constraint = Constraint(
- expr=m.fs.R101.conversion*m.fs.R101.inlet.
- flow_mol_phase_comp[0, "Vap", "toluene"] ==
- (m.fs.R101.inlet.flow_mol_phase_comp[0, "Vap", "toluene"] -
- m.fs.R101.outlet.flow_mol_phase_comp[0, "Vap", "toluene"]))
+ expr=m.fs.R101.conversion
+ * m.fs.R101.inlet.flow_mol_phase_comp[0, "Vap", "toluene"]
+ == (
+ m.fs.R101.inlet.flow_mol_phase_comp[0, "Vap", "toluene"]
+ - m.fs.R101.outlet.flow_mol_phase_comp[0, "Vap", "toluene"]
+ )
+ )
- m.fs.R101.conversion.fix(0.75*pyunits.dimensionless)
- m.fs.R101.heat_duty.fix(0*pyunits.W)
+ m.fs.R101.conversion.fix(0.75 * pyunits.dimensionless)
+ m.fs.R101.heat_duty.fix(0 * pyunits.W)
- m.fs.F101.vap_outlet.temperature.fix(325.0*pyunits.K)
- m.fs.F101.deltaP.fix(0*pyunits.Pa)
+ m.fs.F101.vap_outlet.temperature.fix(325.0 * pyunits.K)
+ m.fs.F101.deltaP.fix(0 * pyunits.Pa)
m.fs.S101.split_fraction[0, "purge"].fix(0.2)
- m.fs.C101.outlet.pressure.fix(350000*pyunits.Pa)
+ m.fs.C101.outlet.pressure.fix(350000 * pyunits.Pa)
- m.fs.H102.outlet.temperature.fix(375*pyunits.K)
- m.fs.H102.deltaP.fix(-200000*pyunits.Pa)
+ m.fs.H102.outlet.temperature.fix(375 * pyunits.K)
+ m.fs.H102.deltaP.fix(-200000 * pyunits.Pa)
# set scaling factors
# Set scaling factors for heat duty, reaction extent and volume
@@ -397,15 +503,17 @@ def hda_with_distillation(tee=True):
iscale.calculate_scaling_factors(m.fs.H102)
# initialize flowsheet
- print('Initializing flowsheet...')
+ print("Initializing flowsheet...")
print()
seq = SequentialDecomposition()
seq.options.select_tear_method = "heuristic"
seq.options.tear_method = "Wegstein"
seq.options.iterLim = 3
- print('Limiting Wegstein tear to 3 iterations to obtain initial solution,'
- ' if not converged IPOPT will pick up and continue.')
+ print(
+ "Limiting Wegstein tear to 3 iterations to obtain initial solution,"
+ " if not converged IPOPT will pick up and continue."
+ )
print()
G = seq.create_graph(m)
@@ -419,26 +527,33 @@ def hda_with_distillation(tee=True):
tear_guesses = {
"flow_mol_phase_comp": {
- (0, "Vap", "benzene"): 1e-5,
- (0, "Vap", "toluene"): 1e-5,
- (0, "Vap", "hydrogen"): 0.30,
- (0, "Vap", "methane"): 0.02,
- (0, "Liq", "benzene"): 1e-5,
- (0, "Liq", "toluene"): 0.30,
- (0, "Liq", "hydrogen"): 1e-5,
- (0, "Liq", "methane"): 1e-5},
+ (0, "Vap", "benzene"): 1e-5,
+ (0, "Vap", "toluene"): 1e-5,
+ (0, "Vap", "hydrogen"): 0.30,
+ (0, "Vap", "methane"): 0.02,
+ (0, "Liq", "benzene"): 1e-5,
+ (0, "Liq", "toluene"): 0.30,
+ (0, "Liq", "hydrogen"): 1e-5,
+ (0, "Liq", "methane"): 1e-5,
+ },
"temperature": {0: 303},
- "pressure": {0: 350000}}
+ "pressure": {0: 350000},
+ }
seq.set_guesses_for(m.fs.H101.inlet, tear_guesses)
def function(unit):
- unit.initialize(outlvl=outlvl)
+ try:
+ initializer = unit.default_initializer()
+ initializer.initialize(unit, output_level=idaeslog.INFO)
+ except InitializationError:
+ solver = get_solver()
+ solver.solve(unit)
seq.run(m, function)
# solve model
- print('Solving flowsheet...')
+ print("Solving flowsheet...")
print()
solver = get_solver()
@@ -446,20 +561,22 @@ def function(unit):
assert results.solver.termination_condition == TerminationCondition.optimal
# add and initialize distilation column, and resolve
- print('Adding distillation column and resolving flowsheet...')
+ print("Adding distillation column and resolving flowsheet...")
print()
from pyomo.common.log import LoggingIntercept
import logging
from io import StringIO
+
stream = StringIO()
with LoggingIntercept(stream, "pyomo.core", logging.WARNING):
m.fs.D101 = TrayColumn(
- number_of_trays=10,
- feed_tray_location=5,
- condenser_type=CondenserType.totalCondenser,
- condenser_temperature_spec=TemperatureSpec.atBubblePoint,
- property_package=m.fs.BT_params)
+ number_of_trays=10,
+ feed_tray_location=5,
+ condenser_type=CondenserType.totalCondenser,
+ condenser_temperature_spec=TemperatureSpec.atBubblePoint,
+ property_package=m.fs.BT_params,
+ )
m.fs.s11 = Arc(source=m.fs.H102.outlet, destination=m.fs.D101.feed)
@@ -467,9 +584,9 @@ def function(unit):
propagate_state(m.fs.s11)
- m.fs.D101.condenser.reflux_ratio.fix(0.5*pyunits.dimensionless)
- m.fs.D101.reboiler.boilup_ratio.fix(0.5*pyunits.dimensionless)
- m.fs.D101.condenser.condenser_pressure.fix(150000*pyunits.Pa)
+ m.fs.D101.condenser.reflux_ratio.fix(0.5 * pyunits.dimensionless)
+ m.fs.D101.reboiler.boilup_ratio.fix(0.5 * pyunits.dimensionless)
+ m.fs.D101.condenser.condenser_pressure.fix(150000 * pyunits.Pa)
# set scaling factors
# Set scaling factors for heat duty
@@ -484,23 +601,26 @@ def function(unit):
assert results.solver.termination_condition == TerminationCondition.optimal
assert_units_consistent(m)
- print('Complete.')
+ print("Complete.")
print()
# adding operating cost expressions
# operating costs for HDA with distillation (model n)
m.fs.cooling_cost = Expression(
- expr=0.25e-7 * (-m.fs.F101.heat_duty[0]) +
- 0.2e-7 * (-m.fs.D101.condenser.heat_duty[0]))
+ expr=0.25e-7 * (-m.fs.F101.heat_duty[0])
+ + 0.2e-7 * (-m.fs.D101.condenser.heat_duty[0])
+ )
m.fs.heating_cost = Expression(
- expr=2.2e-7 * m.fs.H101.heat_duty[0] +
- 1.2e-7 * m.fs.H102.heat_duty[0] +
- 1.9e-7 * m.fs.D101.reboiler.heat_duty[0])
+ expr=2.2e-7 * m.fs.H101.heat_duty[0]
+ + 1.2e-7 * m.fs.H102.heat_duty[0]
+ + 1.9e-7 * m.fs.D101.reboiler.heat_duty[0]
+ )
m.fs.operating_cost = Expression(
- expr=(3600 * 24 * 365 * (m.fs.heating_cost + m.fs.cooling_cost)))
+ expr=(3600 * 24 * 365 * (m.fs.heating_cost + m.fs.cooling_cost))
+ )
# costing block
m.fs.costing = SSLWCosting()
@@ -513,15 +633,14 @@ def function(unit):
flowsheet_costing_block=unit.parent_block().costing,
costing_method=SSLWCostingData.cost_fired_heater,
costing_method_arguments={
- "material_type": HeaterMaterial.CarbonSteel,
- "heat_source": HeaterSource.Fuel,
- }
+ "material_type": HeaterMaterial.CarbonSteel,
+ "heat_source": HeaterSource.Fuel,
+ },
)
# map unit models to unit classes
# will pass to unit_mapping which calls costing methods based on unit class
- unit_class_mapping = {m.fs.R101: CSTR,
- m.fs.F101: Flash}
+ unit_class_mapping = {m.fs.R101: CSTR, m.fs.F101: Flash}
# costing for vessels - m.fs.R101, m.fs.F101
@@ -537,8 +656,9 @@ def function(unit):
unit.length = Var(initialize=1, units=pyunits.m)
if hasattr(unit, "volume"): # if vol exists, set diameter from vol
unit.volume_eq = Constraint(
- expr=unit.volume[0] == unit.length * unit.diameter**2
- * 0.25 * Constants.pi)
+ expr=unit.volume[0]
+ == unit.length * unit.diameter**2 * 0.25 * Constants.pi
+ )
else: # fix diameter directly
unit.diameter.fix(0.2214 * pyunits.m)
# either way, fix L/D to calculate L from D
@@ -549,10 +669,9 @@ def function(unit):
flowsheet_costing_block=unit.parent_block().costing,
costing_method=SSLWCostingData.unit_mapping[unit_class],
costing_method_arguments={
- "material_type": VesselMaterial.CarbonSteel,
- "shell_thickness": 1.25 * pyunits.inch
-
- }
+ "material_type": VesselMaterial.CarbonSteel,
+ "shell_thickness": 1.25 * pyunits.inch,
+ },
)
# costing for column - m.fs.D101
@@ -562,18 +681,17 @@ def function(unit):
m.fs.D101.length = Param(initialize=0.8575, units=pyunits.m)
m.fs.D101.costing = UnitModelCostingBlock(
- flowsheet_costing_block=m.fs.costing,
- costing_method=SSLWCostingData.cost_vertical_vessel,
- costing_method_arguments={
- "material_type": VesselMaterial.CarbonSteel,
- "shell_thickness": 1.25 * pyunits.inch,
- "include_platforms_ladders": True,
- "number_of_trays": m.fs.D101.config.number_of_trays,
- "tray_material": TrayMaterial.CarbonSteel,
- "tray_type": TrayType.Sieve
-
- }
- )
+ flowsheet_costing_block=m.fs.costing,
+ costing_method=SSLWCostingData.cost_vertical_vessel,
+ costing_method_arguments={
+ "material_type": VesselMaterial.CarbonSteel,
+ "shell_thickness": 1.25 * pyunits.inch,
+ "include_platforms_ladders": True,
+ "number_of_trays": m.fs.D101.config.number_of_trays,
+ "tray_material": TrayMaterial.CarbonSteel,
+ "tray_type": TrayType.Sieve,
+ },
+ )
# Check that the degrees of freedom is zero
assert degrees_of_freedom(m) == 0
diff --git a/idaes_examples/notebooks/docs/flowsheets/methanol_flowsheet.py b/idaes_examples/notebooks/docs/flowsheets/methanol_flowsheet.py
index f334bdc7..db61295e 100644
--- a/idaes_examples/notebooks/docs/flowsheets/methanol_flowsheet.py
+++ b/idaes_examples/notebooks/docs/flowsheets/methanol_flowsheet.py
@@ -17,16 +17,18 @@
"""
# Import Pyomo libraries
-from pyomo.environ import (Constraint,
- Objective,
- Var,
- Expression,
- Param,
- ConcreteModel,
- TransformationFactory,
- value,
- maximize,
- units as pyunits)
+from pyomo.environ import (
+ Constraint,
+ Objective,
+ Var,
+ Expression,
+ Param,
+ ConcreteModel,
+ TransformationFactory,
+ value,
+ maximize,
+ units as pyunits,
+)
from pyomo.environ import TerminationCondition
from pyomo.network import Arc
@@ -39,10 +41,12 @@
# Import required models
-from idaes.models.properties.modular_properties.base.generic_property import \
- GenericParameterBlock
-from idaes.models.properties.modular_properties.base.generic_reaction import \
- GenericReactionParameterBlock
+from idaes.models.properties.modular_properties.base.generic_property import (
+ GenericParameterBlock,
+)
+from idaes.models.properties.modular_properties.base.generic_reaction import (
+ GenericReactionParameterBlock,
+)
from idaes_examples.mod.methanol import (
methanol_ideal_VLE as thermo_props_VLE,
@@ -58,7 +62,8 @@
Turbine,
StoichiometricReactor,
Flash,
- Product)
+ Product,
+)
from idaes.models.unit_models.mixer import MomentumMixingType
from idaes.models.unit_models.pressure_changer import ThermodynamicAssumption
from idaes.core import UnitModelCostingBlock
@@ -69,8 +74,7 @@ def build_model(m):
# Define model components and blocks
m.fs = FlowsheetBlock(dynamic=False)
- m.fs.thermo_params_VLE = GenericParameterBlock(
- **thermo_props_VLE.config_dict)
+ m.fs.thermo_params_VLE = GenericParameterBlock(**thermo_props_VLE.config_dict)
m.fs.thermo_params_VLE.set_default_scaling("flow_mol", 1)
m.fs.thermo_params_VLE.set_default_scaling("temperature", 1e-2)
@@ -78,23 +82,20 @@ def build_model(m):
m.fs.thermo_params_VLE.set_default_scaling("enth_mol", 1e-3)
m.fs.thermo_params_VLE.set_default_scaling("entr_mol", 1e-1)
for comp in thermo_props_VLE.config_dict["components"]:
- m.fs.thermo_params_VLE.set_default_scaling("mole_frac_comp",
- 1e2, index=comp)
- m.fs.thermo_params_VLE.set_default_scaling("enth_mol_comp",
- 1e2, index=comp)
- m.fs.thermo_params_VLE.set_default_scaling("enth_mol_phase",
- 1e2, index=comp)
- m.fs.thermo_params_VLE.set_default_scaling("entr_mol_phase_comp",
- 1e2, index=comp)
+ m.fs.thermo_params_VLE.set_default_scaling("mole_frac_comp", 1e2, index=comp)
+ m.fs.thermo_params_VLE.set_default_scaling("enth_mol_comp", 1e2, index=comp)
+ m.fs.thermo_params_VLE.set_default_scaling("enth_mol_phase", 1e2, index=comp)
+ m.fs.thermo_params_VLE.set_default_scaling(
+ "entr_mol_phase_comp", 1e2, index=comp
+ )
for attr in dir(getattr(m.fs.thermo_params_VLE, comp)):
- if 'coef' in attr:
- iscale.set_scaling_factor(getattr(
- getattr(m.fs.thermo_params_VLE, comp), attr), 1)
- m.fs.thermo_params_VLE.set_default_scaling("flow_mol_phase_comp",
- 1, index=comp)
+ if "coef" in attr:
+ iscale.set_scaling_factor(
+ getattr(getattr(m.fs.thermo_params_VLE, comp), attr), 1
+ )
+ m.fs.thermo_params_VLE.set_default_scaling("flow_mol_phase_comp", 1, index=comp)
- m.fs.thermo_params_vapor = GenericParameterBlock(
- **thermo_props_vapor.config_dict)
+ m.fs.thermo_params_vapor = GenericParameterBlock(**thermo_props_vapor.config_dict)
m.fs.thermo_params_vapor.set_default_scaling("flow_mol", 1)
m.fs.thermo_params_vapor.set_default_scaling("temperature", 1e-2)
@@ -102,67 +103,78 @@ def build_model(m):
m.fs.thermo_params_vapor.set_default_scaling("enth_mol", 1e-3)
m.fs.thermo_params_vapor.set_default_scaling("entr_mol", 1e-1)
for comp in thermo_props_vapor.config_dict["components"]:
- m.fs.thermo_params_vapor.set_default_scaling("mole_frac_comp",
- 1e2, index=comp)
- m.fs.thermo_params_vapor.set_default_scaling("enth_mol_comp",
- 1e2, index=comp)
- m.fs.thermo_params_vapor.set_default_scaling("enth_mol_phase",
- 1e2, index=comp)
- m.fs.thermo_params_vapor.set_default_scaling("entr_mol_phase_comp",
- 1e2, index=comp)
+ m.fs.thermo_params_vapor.set_default_scaling("mole_frac_comp", 1e2, index=comp)
+ m.fs.thermo_params_vapor.set_default_scaling("enth_mol_comp", 1e2, index=comp)
+ m.fs.thermo_params_vapor.set_default_scaling("enth_mol_phase", 1e2, index=comp)
+ m.fs.thermo_params_vapor.set_default_scaling(
+ "entr_mol_phase_comp", 1e2, index=comp
+ )
for attr in dir(getattr(m.fs.thermo_params_vapor, comp)):
- if 'coef' in attr:
- iscale.set_scaling_factor(getattr(
- getattr(m.fs.thermo_params_vapor, comp), attr), 1)
- m.fs.thermo_params_vapor.set_default_scaling("flow_mol_phase_comp",
- 1, index=comp)
+ if "coef" in attr:
+ iscale.set_scaling_factor(
+ getattr(getattr(m.fs.thermo_params_vapor, comp), attr), 1
+ )
+ m.fs.thermo_params_vapor.set_default_scaling(
+ "flow_mol_phase_comp", 1, index=comp
+ )
m.fs.reaction_params = GenericReactionParameterBlock(
- property_package=m.fs.thermo_params_vapor,
- **reaction_props.config_dict)
+ property_package=m.fs.thermo_params_vapor, **reaction_props.config_dict
+ )
# feed blocks
m.fs.H2 = Feed(property_package=m.fs.thermo_params_vapor)
m.fs.CO = Feed(property_package=m.fs.thermo_params_vapor)
# mixing feed streams
- m.fs.M101 = Mixer(property_package=m.fs.thermo_params_vapor,
- momentum_mixing_type=MomentumMixingType.minimize,
- has_phase_equilibrium=True,
- inlet_list=['H2_WGS', 'CO_WGS'])
+ m.fs.M101 = Mixer(
+ property_package=m.fs.thermo_params_vapor,
+ momentum_mixing_type=MomentumMixingType.minimize,
+ has_phase_equilibrium=True,
+ inlet_list=["H2_WGS", "CO_WGS"],
+ )
# pre-compression
- m.fs.C101 = Compressor(dynamic=False,
- property_package=m.fs.thermo_params_vapor,
- compressor=True,
- thermodynamic_assumption=ThermodynamicAssumption.isothermal)
+ m.fs.C101 = Compressor(
+ dynamic=False,
+ property_package=m.fs.thermo_params_vapor,
+ compressor=True,
+ thermodynamic_assumption=ThermodynamicAssumption.isothermal,
+ )
# pre-heating
- m.fs.H101 = Heater(property_package=m.fs.thermo_params_vapor,
- has_pressure_change=False,
- has_phase_equilibrium=False)
+ m.fs.H101 = Heater(
+ property_package=m.fs.thermo_params_vapor,
+ has_pressure_change=False,
+ has_phase_equilibrium=False,
+ )
# reactor
- m.fs.R101 = StoichiometricReactor(has_heat_transfer=True,
- has_heat_of_reaction=True,
- has_pressure_change=False,
- property_package=m.fs.thermo_params_vapor,
- reaction_package=m.fs.reaction_params)
+ m.fs.R101 = StoichiometricReactor(
+ has_heat_transfer=True,
+ has_heat_of_reaction=True,
+ has_pressure_change=False,
+ property_package=m.fs.thermo_params_vapor,
+ reaction_package=m.fs.reaction_params,
+ )
# post-expansion
- m.fs.T101 = Turbine(dynamic=False,
- property_package=m.fs.thermo_params_vapor)
+ m.fs.T101 = Turbine(dynamic=False, property_package=m.fs.thermo_params_vapor)
# post-cooling
- m.fs.H102 = Heater(property_package=m.fs.thermo_params_vapor,
- has_pressure_change=False,
- has_phase_equilibrium=False)
+ m.fs.H102 = Heater(
+ property_package=m.fs.thermo_params_vapor,
+ has_pressure_change=False,
+ has_phase_equilibrium=False,
+ )
# product recovery
- m.fs.F101 = Flash(property_package=m.fs.thermo_params_VLE,
- has_heat_transfer=True,
- has_pressure_change=True)
+ m.fs.F101 = Flash(
+ property_package=m.fs.thermo_params_VLE,
+ has_heat_transfer=True,
+ has_pressure_change=True,
+ )
# product blocks
m.fs.EXHAUST = Product(property_package=m.fs.thermo_params_vapor)
@@ -172,13 +184,13 @@ def build_model(m):
# print degrees of freedom for each unit model
# (DOF unit) = (DOF internal) - (DOF feeds)
# since inlet is specified by state properties of feed streams
- print('Unit degrees of freedom')
- for unit in ('M101', 'C101', 'H101', 'R101', 'T101', 'H102', 'F101'):
- if unit == 'M101':
+ print("Unit degrees of freedom")
+ for unit in ("M101", "C101", "H101", "R101", "T101", "H102", "F101"):
+ if unit == "M101":
spec = 14 # (FTP + 4 mole fractions) for both feed streams
else:
spec = 7 # (FTP + 4 mole fractions) for feed stream
- print(str(unit)+' '+str(degrees_of_freedom(getattr(m.fs, unit))-spec))
+ print(str(unit) + " " + str(degrees_of_freedom(getattr(m.fs, unit)) - spec))
# feed streams
m.fs.H2_FEED = Arc(source=m.fs.H2.outlet, destination=m.fs.M101.H2_WGS)
@@ -210,181 +222,200 @@ def build_model(m):
TransformationFactory("network.expand_arcs").apply_to(m)
# Add unit and stream specifications
- print('Total DOF: ', degrees_of_freedom(m))
+ print("Total DOF: ", degrees_of_freedom(m))
return m
def set_inputs(m):
# feed streams, post WGS
- m.fs.H2.outlet.flow_mol[0].fix(637.2*pyunits.mol/pyunits.s)
- m.fs.H2.outlet.mole_frac_comp[0, "H2"].fix(1*pyunits.dimensionless)
- m.fs.H2.outlet.mole_frac_comp[0, "CO"].fix(1e-6*pyunits.dimensionless)
- m.fs.H2.outlet.mole_frac_comp[0, "CH3OH"].fix(1e-6*pyunits.dimensionless)
- m.fs.H2.outlet.mole_frac_comp[0, "CH4"].fix(1e-6*pyunits.dimensionless)
- m.fs.H2.outlet.enth_mol[0].fix(-142.4*pyunits.J/pyunits.mol)
- m.fs.H2.outlet.pressure.fix(30e5*pyunits.Pa)
-
- m.fs.CO.outlet.flow_mol[0].fix(316.8*pyunits.mol/pyunits.s)
- m.fs.CO.outlet.mole_frac_comp[0, "H2"].fix(1e-6*pyunits.dimensionless)
- m.fs.CO.outlet.mole_frac_comp[0, "CO"].fix(1*pyunits.dimensionless)
- m.fs.CO.outlet.mole_frac_comp[0, "CH3OH"].fix(1e-6*pyunits.dimensionless)
- m.fs.CO.outlet.mole_frac_comp[0, "CH4"].fix(1e-6*pyunits.dimensionless)
- m.fs.CO.outlet.enth_mol[0].fix(-110676.4*pyunits.J/pyunits.mol)
- m.fs.CO.outlet.pressure.fix(30e5*pyunits.Pa)
- print('DOF after streams specified: ', degrees_of_freedom(m))
+ m.fs.H2.outlet.flow_mol[0].fix(637.2 * pyunits.mol / pyunits.s)
+ m.fs.H2.outlet.mole_frac_comp[0, "H2"].fix(1 * pyunits.dimensionless)
+ m.fs.H2.outlet.mole_frac_comp[0, "CO"].fix(1e-6 * pyunits.dimensionless)
+ m.fs.H2.outlet.mole_frac_comp[0, "CH3OH"].fix(1e-6 * pyunits.dimensionless)
+ m.fs.H2.outlet.mole_frac_comp[0, "CH4"].fix(1e-6 * pyunits.dimensionless)
+ m.fs.H2.outlet.enth_mol[0].fix(-142.4 * pyunits.J / pyunits.mol)
+ m.fs.H2.outlet.pressure.fix(30e5 * pyunits.Pa)
+
+ m.fs.CO.outlet.flow_mol[0].fix(316.8 * pyunits.mol / pyunits.s)
+ m.fs.CO.outlet.mole_frac_comp[0, "H2"].fix(1e-6 * pyunits.dimensionless)
+ m.fs.CO.outlet.mole_frac_comp[0, "CO"].fix(1 * pyunits.dimensionless)
+ m.fs.CO.outlet.mole_frac_comp[0, "CH3OH"].fix(1e-6 * pyunits.dimensionless)
+ m.fs.CO.outlet.mole_frac_comp[0, "CH4"].fix(1e-6 * pyunits.dimensionless)
+ m.fs.CO.outlet.enth_mol[0].fix(-110676.4 * pyunits.J / pyunits.mol)
+ m.fs.CO.outlet.pressure.fix(30e5 * pyunits.Pa)
+ print("DOF after streams specified: ", degrees_of_freedom(m))
# units specifications
- m.fs.C101.outlet.pressure.fix(51e5*pyunits.Pa)
+ m.fs.C101.outlet.pressure.fix(51e5 * pyunits.Pa)
m.fs.H101.outlet_temp = Constraint(
- expr=m.fs.H101.control_volume.properties_out[0].temperature ==
- 488.15 * pyunits.K)
+ expr=m.fs.H101.control_volume.properties_out[0].temperature
+ == 488.15 * pyunits.K
+ )
m.fs.R101.conversion = Var(initialize=0.75, bounds=(0, 1))
m.fs.R101.conv_constraint = Constraint(
- expr=(m.fs.R101.conversion * m.fs.R101.inlet.flow_mol[0] *
- m.fs.R101.inlet.mole_frac_comp[0, "CO"] ==
- m.fs.R101.inlet.flow_mol[0] *
- m.fs.R101.inlet.mole_frac_comp[0, "CO"]
- - m.fs.R101.outlet.flow_mol[0] *
- m.fs.R101.outlet.mole_frac_comp[0, "CO"]))
- m.fs.R101.conversion.fix(0.75*pyunits.dimensionless)
+ expr=(
+ m.fs.R101.conversion
+ * m.fs.R101.inlet.flow_mol[0]
+ * m.fs.R101.inlet.mole_frac_comp[0, "CO"]
+ == m.fs.R101.inlet.flow_mol[0] * m.fs.R101.inlet.mole_frac_comp[0, "CO"]
+ - m.fs.R101.outlet.flow_mol[0] * m.fs.R101.outlet.mole_frac_comp[0, "CO"]
+ )
+ )
+ m.fs.R101.conversion.fix(0.75 * pyunits.dimensionless)
m.fs.R101.outlet_temp = Constraint(
- expr=m.fs.R101.control_volume.properties_out[0].temperature ==
- 507.15 * pyunits.K)
+ expr=m.fs.R101.control_volume.properties_out[0].temperature
+ == 507.15 * pyunits.K
+ )
# rxn is exothermic, so duty is cooling only
- m.fs.R101.heat_duty.setub(0*pyunits.W)
+ m.fs.R101.heat_duty.setub(0 * pyunits.W)
- m.fs.T101.deltaP.fix(-2e6*pyunits.Pa)
- m.fs.T101.efficiency_isentropic.fix(0.9*pyunits.dimensionless)
+ m.fs.T101.deltaP.fix(-2e6 * pyunits.Pa)
+ m.fs.T101.efficiency_isentropic.fix(0.9 * pyunits.dimensionless)
m.fs.H102.outlet_temp = Constraint(
- expr=m.fs.H102.control_volume.properties_out[0].temperature ==
- 407.15 * pyunits.K)
+ expr=m.fs.H102.control_volume.properties_out[0].temperature
+ == 407.15 * pyunits.K
+ )
m.fs.F101.recovery = Var(initialize=0.01, bounds=(0, 1))
m.fs.F101.rec_constraint = Constraint(
- expr=(m.fs.F101.recovery == m.fs.F101.liq_outlet.flow_mol[0] *
- m.fs.F101.liq_outlet.mole_frac_comp[0, "CH3OH"] /
- (m.fs.F101.inlet.flow_mol[0] *
- m.fs.F101.inlet.mole_frac_comp[0, "CH3OH"])))
- m.fs.F101.deltaP.fix(0*pyunits.Pa)
+ expr=(
+ m.fs.F101.recovery
+ == m.fs.F101.liq_outlet.flow_mol[0]
+ * m.fs.F101.liq_outlet.mole_frac_comp[0, "CH3OH"]
+ / (m.fs.F101.inlet.flow_mol[0] * m.fs.F101.inlet.mole_frac_comp[0, "CH3OH"])
+ )
+ )
+ m.fs.F101.deltaP.fix(0 * pyunits.Pa)
m.fs.F101.outlet_temp = Constraint(
- expr=m.fs.F101.control_volume.properties_out[0].temperature ==
- 407.15 * pyunits.K)
+ expr=m.fs.F101.control_volume.properties_out[0].temperature
+ == 407.15 * pyunits.K
+ )
- print('DOF after units specified: ', degrees_of_freedom(m))
+ print("DOF after units specified: ", degrees_of_freedom(m))
def scale_flowsheet(m):
for var in m.fs.component_data_objects(Var, descend_into=True):
- if 'flow_mol' in var.name:
+ if "flow_mol" in var.name:
iscale.set_scaling_factor(var, 1)
- if 'temperature' in var.name:
+ if "temperature" in var.name:
iscale.set_scaling_factor(var, 1e-2)
- if 'pressure' in var.name:
+ if "pressure" in var.name:
iscale.set_scaling_factor(var, 1e-2)
- if 'enth_mol' in var.name:
+ if "enth_mol" in var.name:
iscale.set_scaling_factor(var, 1e-3)
- if 'enth_mol_phase' in var.name:
+ if "enth_mol_phase" in var.name:
iscale.set_scaling_factor(var, 1e-3)
- if 'mole_frac' in var.name:
+ if "mole_frac" in var.name:
iscale.set_scaling_factor(var, 1e2)
- if 'entr_mol' in var.name:
+ if "entr_mol" in var.name:
iscale.set_scaling_factor(var, 1e-1)
- if 'rate_reaction_extent' in var.name:
+ if "rate_reaction_extent" in var.name:
iscale.set_scaling_factor(var, 1e-3)
- if 'heat' in var.name:
+ if "heat" in var.name:
iscale.set_scaling_factor(var, 1e-3)
- if 'work' in var.name:
+ if "work" in var.name:
iscale.set_scaling_factor(var, 1e-3)
# manual scaling
- for unit in ('H2', 'CO', 'M101', 'C101', 'H101',
- 'R101', 'T101', 'H102', 'F101', 'CH3OH'):
+ for unit in (
+ "H2",
+ "CO",
+ "M101",
+ "C101",
+ "H101",
+ "R101",
+ "T101",
+ "H102",
+ "F101",
+ "CH3OH",
+ ):
block = getattr(m.fs, unit)
if hasattr(block, "control_volume"):
iscale.set_scaling_factor(
- block.control_volume.properties_in[0.0].mole_frac_comp, 1e2)
+ block.control_volume.properties_in[0.0].mole_frac_comp, 1e2
+ )
iscale.set_scaling_factor(
- block.control_volume.properties_out[0.0].mole_frac_comp, 1e2)
+ block.control_volume.properties_out[0.0].mole_frac_comp, 1e2
+ )
iscale.set_scaling_factor(
- block.control_volume.properties_in[0.0].enth_mol_phase, 1e2)
+ block.control_volume.properties_in[0.0].enth_mol_phase, 1e2
+ )
iscale.set_scaling_factor(
- block.control_volume.properties_out[0.0].enth_mol_phase, 1e2)
+ block.control_volume.properties_out[0.0].enth_mol_phase, 1e2
+ )
if hasattr(block.control_volume, "rate_reaction_extent"):
- iscale.set_scaling_factor(block.control_volume
- .rate_reaction_extent, 1e3)
+ iscale.set_scaling_factor(
+ block.control_volume.rate_reaction_extent, 1e3
+ )
if hasattr(block.control_volume, "heat"):
iscale.set_scaling_factor(block.control_volume.heat, 1e-3)
if hasattr(block.control_volume, "work"):
iscale.set_scaling_factor(block.control_volume.work, 1e-3)
if hasattr(block, "properties_isentropic"):
iscale.set_scaling_factor(
- block.properties_isentropic[0.0].mole_frac_comp, 1e2)
+ block.properties_isentropic[0.0].mole_frac_comp, 1e2
+ )
iscale.set_scaling_factor(
- block.properties_isentropic[0.0].enth_mol_phase, 1e-3)
+ block.properties_isentropic[0.0].enth_mol_phase, 1e-3
+ )
if hasattr(block, "properties"):
- iscale.set_scaling_factor(
- block.properties[0.0].mole_frac_comp, 1e2)
+ iscale.set_scaling_factor(block.properties[0.0].mole_frac_comp, 1e2)
if hasattr(block, "split"):
- iscale.set_scaling_factor(
- block.split._Liq_flow_mol_ref, 1)
- iscale.set_scaling_factor(
- block.split._Vap_flow_mol_ref, 1)
- iscale.set_scaling_factor(
- block.split._Liq_mole_frac_comp_ref, 1e2)
- iscale.set_scaling_factor(
- block.split._Vap_mole_frac_comp_ref, 1e2)
- iscale.set_scaling_factor(
- block.split._Liq_enth_mol_ref, 1e-3)
- iscale.set_scaling_factor(
- block.split._Vap_enth_mol_ref, 1e-3)
+ iscale.set_scaling_factor(block.split._Liq_flow_mol_ref, 1)
+ iscale.set_scaling_factor(block.split._Vap_flow_mol_ref, 1)
+ iscale.set_scaling_factor(block.split._Liq_mole_frac_comp_ref, 1e2)
+ iscale.set_scaling_factor(block.split._Vap_mole_frac_comp_ref, 1e2)
+ iscale.set_scaling_factor(block.split._Liq_enth_mol_ref, 1e-3)
+ iscale.set_scaling_factor(block.split._Vap_enth_mol_ref, 1e-3)
# set scaling for unit constraints
- for name in ('M101', 'C101', 'H101', 'R101', 'T101', 'H102', 'F101'):
+ for name in ("M101", "C101", "H101", "R101", "T101", "H102", "F101"):
unit = getattr(m.fs, name)
# mixer constraints
- if hasattr(unit, 'material_mixing_equations'):
+ if hasattr(unit, "material_mixing_equations"):
for (t, j), c in unit.material_mixing_equations.items():
iscale.constraint_scaling_transform(c, 1, overwrite=False)
- if hasattr(unit, 'enthalpy_mixing_equations'):
+ if hasattr(unit, "enthalpy_mixing_equations"):
for t, c in unit.enthalpy_mixing_equations.items():
iscale.constraint_scaling_transform(c, 1e-3, overwrite=False)
- if hasattr(unit, 'minimum_pressure_constraint'):
+ if hasattr(unit, "minimum_pressure_constraint"):
for (t, i), c in unit.minimum_pressure_constraint.items():
iscale.constraint_scaling_transform(c, 1e-2, overwrite=False)
- if hasattr(unit, 'mixture_pressure'):
+ if hasattr(unit, "mixture_pressure"):
for t, c in unit.mixture_pressure.items():
iscale.constraint_scaling_transform(c, 1e-2, overwrite=False)
- if hasattr(unit, 'pressure_equality_constraints'):
+ if hasattr(unit, "pressure_equality_constraints"):
for (t, i), c in unit.pressure_equality_constraints.items():
iscale.constraint_scaling_transform(c, 1e-5, overwrite=False)
# splitter constraints
- if hasattr(unit, 'material_splitting_eqn'):
+ if hasattr(unit, "material_splitting_eqn"):
for (t, o, j), c in unit.material_splitting_eqn.items():
iscale.constraint_scaling_transform(c, 1, overwrite=False)
- if hasattr(unit, 'temperature_equality_eqn'):
+ if hasattr(unit, "temperature_equality_eqn"):
for (t, o), c in unit.temperature_equality_eqn.items():
iscale.constraint_scaling_transform(c, 1e-2, overwrite=False)
- if hasattr(unit, 'molar_enthalpy_equality_eqn'):
+ if hasattr(unit, "molar_enthalpy_equality_eqn"):
for (t, o), c in unit.molar_enthalpy_equality_eqn.items():
iscale.constraint_scaling_transform(c, 1e-3, overwrite=False)
- if hasattr(unit, 'molar_enthalpy_splitting_eqn'):
+ if hasattr(unit, "molar_enthalpy_splitting_eqn"):
for (t, o), c in unit.molar_enthalpy_splitting_eqn.items():
iscale.constraint_scaling_transform(c, 1e-3, overwrite=False)
- if hasattr(unit, 'pressure_equality_eqn'):
+ if hasattr(unit, "pressure_equality_eqn"):
for (t, o), c in unit.pressure_equality_eqn.items():
iscale.constraint_scaling_transform(c, 1e-5, overwrite=False)
- if hasattr(unit, 'sum_split_frac'):
+ if hasattr(unit, "sum_split_frac"):
for t, c in unit.sum_split_frac.items():
iscale.constraint_scaling_transform(c, 1e2, overwrite=False)
# flash adds same as splitter, plus one more
- if hasattr(unit, 'split_fraction_eq'):
+ if hasattr(unit, "split_fraction_eq"):
for (t, o), c in unit.split_fraction_eq.items():
iscale.constraint_scaling_transform(c, 1e2, overwrite=False)
@@ -427,41 +458,41 @@ def scale_flowsheet(m):
iscale.constraint_scaling_transform(c, 1e-2, overwrite=False)
# heater and reactor only add 0D control volume constraints
- if hasattr(unit, 'material_holdup_calculation'):
+ if hasattr(unit, "material_holdup_calculation"):
for (t, p, j), c in unit.material_holdup_calculation.items():
iscale.constraint_scaling_transform(c, 1, overwrite=False)
- if hasattr(unit, 'rate_reaction_stoichiometry_constraint'):
- for (t, p, j), c in (
- unit.rate_reaction_stoichiometry_constraint.items()):
+ if hasattr(unit, "rate_reaction_stoichiometry_constraint"):
+ for (t, p, j), c in unit.rate_reaction_stoichiometry_constraint.items():
iscale.constraint_scaling_transform(c, 1, overwrite=False)
- if hasattr(unit, 'equilibrium_reaction_stoichiometry_constraint'):
- for (t, p, j), c in (
- unit.equilibrium_reaction_stoichiometry_constraint
- .items()):
+ if hasattr(unit, "equilibrium_reaction_stoichiometry_constraint"):
+ for (
+ t,
+ p,
+ j,
+ ), c in unit.equilibrium_reaction_stoichiometry_constraint.items():
iscale.constraint_scaling_transform(c, 1, overwrite=False)
- if hasattr(unit, 'inherent_reaction_stoichiometry_constraint'):
- for (t, p, j), c in (
- unit.inherent_reaction_stoichiometry_constraint.items()):
+ if hasattr(unit, "inherent_reaction_stoichiometry_constraint"):
+ for (t, p, j), c in unit.inherent_reaction_stoichiometry_constraint.items():
iscale.constraint_scaling_transform(c, 1, overwrite=False)
- if hasattr(unit, 'material_balances'):
+ if hasattr(unit, "material_balances"):
for (t, p, j), c in unit.material_balances.items():
iscale.constraint_scaling_transform(c, 1, overwrite=False)
- if hasattr(unit, 'element_balances'):
+ if hasattr(unit, "element_balances"):
for (t, e), c in unit.element_balances.items():
iscale.constraint_scaling_transform(c, 1, overwrite=False)
- if hasattr(unit, 'elemental_holdup_calculation'):
+ if hasattr(unit, "elemental_holdup_calculation"):
for (t, e), c in unit.elemental_holdup_calculation.items():
iscale.constraint_scaling_transform(c, 1, overwrite=False)
- if hasattr(unit, 'enthalpy_balances'):
+ if hasattr(unit, "enthalpy_balances"):
for t, c in unit.enthalpy_balances.items():
iscale.constraint_scaling_transform(c, 1e-3, overwrite=False)
- if hasattr(unit, 'energy_holdup_calculation'):
+ if hasattr(unit, "energy_holdup_calculation"):
for (t, p), c in unit.energy_holdup_calculation.items():
iscale.constraint_scaling_transform(c, 1e-3, overwrite=False)
- if hasattr(unit, 'pressure_balance'):
+ if hasattr(unit, "pressure_balance"):
for t, c in unit.pressure_balance.items():
iscale.constraint_scaling_transform(c, 1e-5, overwrite=False)
- if hasattr(unit, 'sum_of_phase_fractions'):
+ if hasattr(unit, "sum_of_phase_fractions"):
for t, c in unit.sum_of_phase_fractions.items():
iscale.constraint_scaling_transform(c, 1e2, overwrite=False)
if hasattr(unit, "material_accumulation_disc_eq"):
@@ -481,27 +512,20 @@ def scale_flowsheet(m):
for c in arc.component_data_objects(Constraint, descend_into=True):
if hasattr(unit, "enth_mol_equality"):
for t, c in unit.enth_mol_equality.items():
- iscale.constraint_scaling_transform(c, 1e-3,
- overwrite=False)
+ iscale.constraint_scaling_transform(c, 1e-3, overwrite=False)
if hasattr(unit, "flow_mol_equality"):
for t, c in unit.flow_mol_equality.items():
- iscale.constraint_scaling_transform(c, 1,
- overwrite=False)
+ iscale.constraint_scaling_transform(c, 1, overwrite=False)
if hasattr(unit, "mole_frac_comp_equality"):
for (t, j), c in unit.mole_frac_comp_equality.items():
- iscale.constraint_scaling_transform(c, 1e2,
- overwrite=False)
+ iscale.constraint_scaling_transform(c, 1e2, overwrite=False)
if hasattr(unit, "pressure_equality"):
for t, c in unit.pressure_equality.items():
- iscale.constraint_scaling_transform(c, 1e-5,
- overwrite=False)
+ iscale.constraint_scaling_transform(c, 1e-5, overwrite=False)
- iscale.set_scaling_factor(
- m.fs.M101.H2_WGS_state[0.0].enth_mol_phase['Vap'], 1e-3)
- iscale.set_scaling_factor(
- m.fs.M101.CO_WGS_state[0.0].enth_mol_phase['Vap'], 1e-3)
- iscale.set_scaling_factor(
- m.fs.M101.mixed_state[0.0].enth_mol_phase['Vap'], 1e-3)
+ iscale.set_scaling_factor(m.fs.M101.H2_WGS_state[0.0].enth_mol_phase["Vap"], 1e-3)
+ iscale.set_scaling_factor(m.fs.M101.CO_WGS_state[0.0].enth_mol_phase["Vap"], 1e-3)
+ iscale.set_scaling_factor(m.fs.M101.mixed_state[0.0].enth_mol_phase["Vap"], 1e-3)
iscale.calculate_scaling_factors(m)
@@ -510,7 +534,7 @@ def initialize_flowsheet(m):
# Initialize and solve flowsheet
- print('')
+ print("")
m.fs.H2.initialize()
propagate_state(arc=m.fs.H2_FEED)
@@ -553,87 +577,109 @@ def add_costing(m):
m.fs.cooling_cost = Expression(
expr=0.25e-7 * (-m.fs.F101.heat_duty[0])
+ 0.212e-7 * (-m.fs.H102.heat_duty[0])
- + 2.2e-7 * (-m.fs.R101.heat_duty[0]))
+ + 2.2e-7 * (-m.fs.R101.heat_duty[0])
+ )
# Expression to compute the total heating cost (F/R heating not assumed)
- m.fs.heating_cost = Expression(
- expr=2.2e-7 * m.fs.H101.heat_duty[0])
+ m.fs.heating_cost = Expression(expr=2.2e-7 * m.fs.H101.heat_duty[0])
# Expression to compute the total electricity cost (utilities - credit)
m.fs.electricity_cost = Expression(
expr=0.12e-5 * (m.fs.C101.work_mechanical[0])
- - 0.08e-5 * (m.fs.T101.work_isentropic[0]))
+ - 0.08e-5 * (m.fs.T101.work_isentropic[0])
+ )
# Expression to compute the total operating cost
m.fs.operating_cost = Expression(
- expr=(3600 * 24 * 365 * (m.fs.heating_cost + m.fs.cooling_cost
- + m.fs.electricity_cost)))
+ expr=(
+ 3600
+ * 24
+ * 365
+ * (m.fs.heating_cost + m.fs.cooling_cost + m.fs.electricity_cost)
+ )
+ )
# Computing reactor capital cost
m.fs.R101.diameter = Param(initialize=2, units=pyunits.m)
m.fs.R101.length = Var(initialize=4, units=pyunits.m)
m.fs.R101.diameter = 2 * pyunits.m
- m.fs.R101.length.fix(4*pyunits.m) # for initial problem at 75% conversion
- m.fs.R101.costing = UnitModelCostingBlock(
- flowsheet_costing_block=m.fs.costing)
+ m.fs.R101.length.fix(4 * pyunits.m) # for initial problem at 75% conversion
+ m.fs.R101.costing = UnitModelCostingBlock(flowsheet_costing_block=m.fs.costing)
# surrogate model which scales length linearly with conversion
m.fs.R101.length.unfix()
- m.fs.R101.L_eq = Constraint(expr=m.fs.R101.length ==
- 13.2000*pyunits.m*m.fs.R101.conversion -
- 5.9200*pyunits.m)
+ m.fs.R101.L_eq = Constraint(
+ expr=m.fs.R101.length
+ == 13.2000 * pyunits.m * m.fs.R101.conversion - 5.9200 * pyunits.m
+ )
# Computing flash capital cost
m.fs.F101.diameter = Param(initialize=2, units=pyunits.m)
m.fs.F101.length = Param(initialize=4, units=pyunits.m)
m.fs.F101.diameter = 2 * pyunits.m
m.fs.F101.length = 4 * pyunits.m
- m.fs.F101.costing = UnitModelCostingBlock(
- flowsheet_costing_block=m.fs.costing)
+ m.fs.F101.costing = UnitModelCostingBlock(flowsheet_costing_block=m.fs.costing)
# Computing heater/cooler capital costs
# Surrogates prepared with IDAES shell and tube hx considering IP steam and
# assuming steam outlet is condensed
m.fs.H101.cost_heater = Expression(
- expr=0.036158*m.fs.H101.heat_duty[0] + 63931.475*pyunits.W,
- doc='capital cost of heater in $')
+ expr=0.036158 * m.fs.H101.heat_duty[0] + 63931.475 * pyunits.W,
+ doc="capital cost of heater in $",
+ )
# Surrogates prepared with IDAES shell and tube hx considering cooling
# water assuming that water inlet T is 25 deg C and outlet T is 40 deg C
m.fs.H102.cost_heater = Expression(
- expr=0.10230*(-m.fs.H102.heat_duty[0]) + 100421.572*pyunits.W,
- doc='capital cost of cooler in $')
+ expr=0.10230 * (-m.fs.H102.heat_duty[0]) + 100421.572 * pyunits.W,
+ doc="capital cost of cooler in $",
+ )
# Annualizing capital cost to same scale as operating costs (per year)
m.fs.annualized_capital_cost = Expression(
- expr=(m.fs.R101.costing.capital_cost
- + m.fs.F101.costing.capital_cost
- + m.fs.H101.cost_heater
- + m.fs.H102.cost_heater)*5.4/15)
+ expr=(
+ m.fs.R101.costing.capital_cost
+ + m.fs.F101.costing.capital_cost
+ + m.fs.H101.cost_heater
+ + m.fs.H102.cost_heater
+ )
+ * 5.4
+ / 15
+ )
# methanol price $449 us dollars per metric ton - 32.042 g/mol
# - 1 gr = 1e-6 MT -- consider 1000
# H2 $16.51 per kilogram - 2.016 g/mol
# CO $62.00 per kilogram - 28.01 g/mol
m.fs.sales = Expression(
- expr=(3600 * 24 * 365
- * m.fs.CH3OH.inlet.flow_mol[0]
- * m.fs.CH3OH.inlet.mole_frac_comp[0, "CH3OH"]
- * 32.042 * 1e-6 * 449 * 1000
- )
+ expr=(
+ 3600
+ * 24
+ * 365
+ * m.fs.CH3OH.inlet.flow_mol[0]
+ * m.fs.CH3OH.inlet.mole_frac_comp[0, "CH3OH"]
+ * 32.042
+ * 1e-6
+ * 449
+ * 1000
)
+ )
m.fs.raw_mat_cost = Expression(
- expr=(3600 * 24 * 365 * m.fs.CO.outlet.flow_mol[0] *
- 16.51 * 2.016 / 1000
- + 3600 * 24 * 365 * m.fs.H2.outlet.flow_mol[0] *
- 62.00 * 28.01 / 1000
- )
+ expr=(
+ 3600 * 24 * 365 * m.fs.CO.outlet.flow_mol[0] * 16.51 * 2.016 / 1000
+ + 3600 * 24 * 365 * m.fs.H2.outlet.flow_mol[0] * 62.00 * 28.01 / 1000
)
-
- m.fs.objective = Objective(expr=(m.fs.sales
- - m.fs.operating_cost
- - m.fs.annualized_capital_cost
- - m.fs.raw_mat_cost)/1e3,
- sense=maximize)
+ )
+
+ m.fs.objective = Objective(
+ expr=(
+ m.fs.sales
+ - m.fs.operating_cost
+ - m.fs.annualized_capital_cost
+ - m.fs.raw_mat_cost
+ )
+ / 1e3,
+ sense=maximize,
+ )
assert degrees_of_freedom(m) == 0
@@ -645,56 +691,72 @@ def report(m):
print()
extent = m.fs.R101.rate_reaction_extent[0, "R1"] # shorter parameter alias
- print('Extent of reaction: ', value(extent))
- print('Stoichiometry of each component normalized by the extent:')
- complist = ('CH4', 'H2', 'CH3OH', 'CO')
- changelist = [value(m.fs.R101.outlet.mole_frac_comp[0, comp] *
- m.fs.R101.outlet.flow_mol[0] -
- m.fs.R101.inlet.mole_frac_comp[0, comp] *
- m.fs.R101.inlet.flow_mol[0]) for comp in complist]
- normlist = [changelist[i]/value(extent) for i in range(len(complist))]
+ print("Extent of reaction: ", value(extent))
+ print("Stoichiometry of each component normalized by the extent:")
+ complist = ("CH4", "H2", "CH3OH", "CO")
+ changelist = [
+ value(
+ m.fs.R101.outlet.mole_frac_comp[0, comp] * m.fs.R101.outlet.flow_mol[0]
+ - m.fs.R101.inlet.mole_frac_comp[0, comp] * m.fs.R101.inlet.flow_mol[0]
+ )
+ for comp in complist
+ ]
+ normlist = [changelist[i] / value(extent) for i in range(len(complist))]
change = dict(zip(complist, normlist))
for entry in change:
- print(entry, ': ', round(change[entry], 2))
- print('These coefficients should follow 1*CO + 2*H2 => 1*CH3OH')
+ print(entry, ": ", round(change[entry], 2))
+ print("These coefficients should follow 1*CO + 2*H2 => 1*CH3OH")
print()
- print('Reaction conversion: ', m.fs.R101.conversion.value)
- print('Reactor duty (MW): ', m.fs.R101.heat_duty[0].value/1e6)
- print('Duty from Reaction (MW)):', value(extent) *
- -m.fs.reaction_params.reaction_R1.dh_rxn_ref.value/1e6)
- print('Turbine work (MW): ', m.fs.T101.work_isentropic[0].value/1e6)
- print('Mixer outlet temperature (C)): ',
- m.fs.M101.mixed_state[0].temperature.value - 273.15)
- print('Compressor outlet temperature (C)): ',
- m.fs.C101.control_volume.properties_out[0].temperature.value
- - 273.15)
- print('Compressor outlet pressure (Pa)): ',
- m.fs.C101.control_volume.properties_out[0].pressure.value)
- print('Heater outlet temperature (C)): ',
- m.fs.H101.control_volume.properties_out[0].temperature.value
- - 273.15)
- print('Reactor outlet temperature (C)): ',
- m.fs.R101.control_volume.properties_out[0].temperature.value
- - 273.15)
- print('Turbine outlet temperature (C)): ',
- m.fs.T101.control_volume.properties_out[0].temperature.value
- - 273.15)
- print('Turbine outlet pressure (Pa)): ',
- m.fs.T101.control_volume.properties_out[0].pressure.value)
- print('Cooler outlet temperature (C)): ',
- m.fs.H102.control_volume.properties_out[0].temperature.value
- - 273.15)
- print('Flash outlet temperature (C)): ',
- m.fs.F101.control_volume.properties_out[0].temperature.value
- - 273.15)
- print('Methanol recovery(%): ', value(100*m.fs.F101.recovery))
- print('annualized capital cost ($/year) =',
- value(m.fs.annualized_capital_cost))
- print('operating cost ($/year) = ', value(m.fs.operating_cost))
- print('sales ($/year) = ', value(m.fs.sales))
- print('raw materials cost ($/year) =', value(m.fs.raw_mat_cost))
- print('revenue (1000$/year)= ', value(m.fs.objective))
+ print("Reaction conversion: ", m.fs.R101.conversion.value)
+ print("Reactor duty (MW): ", m.fs.R101.heat_duty[0].value / 1e6)
+ print(
+ "Duty from Reaction (MW)):",
+ value(extent) * -m.fs.reaction_params.reaction_R1.dh_rxn_ref.value / 1e6,
+ )
+ print("Turbine work (MW): ", m.fs.T101.work_isentropic[0].value / 1e6)
+ print(
+ "Mixer outlet temperature (C)): ",
+ m.fs.M101.mixed_state[0].temperature.value - 273.15,
+ )
+ print(
+ "Compressor outlet temperature (C)): ",
+ m.fs.C101.control_volume.properties_out[0].temperature.value - 273.15,
+ )
+ print(
+ "Compressor outlet pressure (Pa)): ",
+ m.fs.C101.control_volume.properties_out[0].pressure.value,
+ )
+ print(
+ "Heater outlet temperature (C)): ",
+ m.fs.H101.control_volume.properties_out[0].temperature.value - 273.15,
+ )
+ print(
+ "Reactor outlet temperature (C)): ",
+ m.fs.R101.control_volume.properties_out[0].temperature.value - 273.15,
+ )
+ print(
+ "Turbine outlet temperature (C)): ",
+ m.fs.T101.control_volume.properties_out[0].temperature.value - 273.15,
+ )
+ print(
+ "Turbine outlet pressure (Pa)): ",
+ m.fs.T101.control_volume.properties_out[0].pressure.value,
+ )
+ print(
+ "Cooler outlet temperature (C)): ",
+ m.fs.H102.control_volume.properties_out[0].temperature.value - 273.15,
+ )
+ print(
+ "Flash outlet temperature (C)): ",
+ m.fs.F101.control_volume.properties_out[0].temperature.value - 273.15,
+ )
+ print("Methanol recovery(%): ", value(100 * m.fs.F101.recovery))
+ print("annualized capital cost ($/year) =", value(m.fs.annualized_capital_cost))
+ print("operating cost ($/year) = ", value(m.fs.operating_cost))
+ print("sales ($/year) = ", value(m.fs.sales))
+ print("raw materials cost ($/year) =", value(m.fs.raw_mat_cost))
+ print("revenue (1000$/year)= ", value(m.fs.objective))
print()
m.fs.H2.report()
@@ -705,25 +767,23 @@ def report(m):
def main(m):
solver = get_solver() # IPOPT
- optarg = {'tol': 1e-6,
- 'max_iter': 500}
+ optarg = {"tol": 1e-6, "max_iter": 500}
solver.options = optarg
build_model(m) # build flowsheet
set_inputs(m) # unit and stream specifications
scale_flowsheet(m)
initialize_flowsheet(m) # rigorous initialization scheme
- print('DOF before solve: ', degrees_of_freedom(m))
+ print("DOF before solve: ", degrees_of_freedom(m))
print()
- print('Solving initial problem...')
+ print("Solving initial problem...")
results = solver.solve(m, tee=True)
assert results.solver.termination_condition == TerminationCondition.optimal
add_costing(m) # re-solve with costing equations
print()
results2 = solver.solve(m, tee=True)
- assert results2.solver.termination_condition == \
- TerminationCondition.optimal
- print('Initial solution process results:')
+ assert results2.solver.termination_condition == TerminationCondition.optimal
+ print("Initial solution process results:")
report(m) # display initial solution results
# Set up Optimization Problem (Maximize Revenue)
@@ -733,39 +793,43 @@ def main(m):
m.fs.R101.conversion_ub = Constraint(expr=m.fs.R101.conversion <= 0.85)
m.fs.R101.outlet_temp.deactivate()
m.fs.R101.outlet_t_lb = Constraint(
- expr=m.fs.R101.control_volume.properties_out[0.0].temperature
- >= 405*pyunits.K)
+ expr=m.fs.R101.control_volume.properties_out[0.0].temperature >= 405 * pyunits.K
+ )
m.fs.R101.outlet_t_ub = Constraint(
- expr=m.fs.R101.control_volume.properties_out[0.0].temperature
- <= 505*pyunits.K)
+ expr=m.fs.R101.control_volume.properties_out[0.0].temperature <= 505 * pyunits.K
+ )
# Optimize turbine work (or delta P)
m.fs.T101.deltaP.unfix() # optimize turbine work recovery/pressure drop
m.fs.T101.outlet_p_lb = Constraint(
- expr=m.fs.T101.outlet.pressure[0] >= 10E5*pyunits.Pa)
+ expr=m.fs.T101.outlet.pressure[0] >= 10e5 * pyunits.Pa
+ )
m.fs.T101.outlet_p_ub = Constraint(
- expr=m.fs.T101.outlet.pressure[0] <= 51E5*0.8*pyunits.Pa)
+ expr=m.fs.T101.outlet.pressure[0] <= 51e5 * 0.8 * pyunits.Pa
+ )
# Optimize Cooler outlet temperature - unfix cooler outlet temperature
m.fs.H102.outlet_temp.deactivate()
m.fs.H102.outlet_t_lb = Constraint(
expr=m.fs.H102.control_volume.properties_out[0.0].temperature
- >= 407.15*0.8*pyunits.K)
+ >= 407.15 * 0.8 * pyunits.K
+ )
m.fs.H102.outlet_t_ub = Constraint(
- expr=m.fs.H102.control_volume.properties_out[0.0].temperature
- <= 480*pyunits.K)
+ expr=m.fs.H102.control_volume.properties_out[0.0].temperature <= 480 * pyunits.K
+ )
m.fs.F101.deltaP.unfix() # allow pressure change in streams
m.fs.F101.isothermal = Constraint(
- expr=m.fs.F101.control_volume.properties_out[0].temperature ==
- m.fs.F101.control_volume.properties_in[0].temperature)
+ expr=m.fs.F101.control_volume.properties_out[0].temperature
+ == m.fs.F101.control_volume.properties_in[0].temperature
+ )
print()
- print('Solving optimization problem...')
+ print("Solving optimization problem...")
opt_res = solver.solve(m, tee=True)
assert opt_res.solver.termination_condition == TerminationCondition.optimal
- print('Optimal solution process results:')
+ print("Optimal solution process results:")
report(m)
return m
diff --git a/idaes_examples/notebooks/docs/flowsheets/methanol_flowsheet_w_recycle.py b/idaes_examples/notebooks/docs/flowsheets/methanol_flowsheet_w_recycle.py
index 5334d803..8d5cfd7c 100644
--- a/idaes_examples/notebooks/docs/flowsheets/methanol_flowsheet_w_recycle.py
+++ b/idaes_examples/notebooks/docs/flowsheets/methanol_flowsheet_w_recycle.py
@@ -17,16 +17,18 @@
"""
# Import Pyomo libraries
-from pyomo.environ import (Constraint,
- Objective,
- Var,
- Expression,
- Param,
- ConcreteModel,
- TransformationFactory,
- value,
- maximize,
- units as pyunits)
+from pyomo.environ import (
+ Constraint,
+ Objective,
+ Var,
+ Expression,
+ Param,
+ ConcreteModel,
+ TransformationFactory,
+ value,
+ maximize,
+ units as pyunits,
+)
from pyomo.environ import TerminationCondition
from pyomo.network import Arc, SequentialDecomposition
@@ -39,10 +41,12 @@
# Import required models
-from idaes.models.properties.modular_properties.base.generic_property import \
- GenericParameterBlock
-from idaes.models.properties.modular_properties.base.generic_reaction import \
- GenericReactionParameterBlock
+from idaes.models.properties.modular_properties.base.generic_property import (
+ GenericParameterBlock,
+)
+from idaes.models.properties.modular_properties.base.generic_reaction import (
+ GenericReactionParameterBlock,
+)
from idaes_examples.mod.methanol import (
methanol_ideal_VLE as thermo_props_VLE,
@@ -59,7 +63,8 @@
StoichiometricReactor,
Flash,
Separator as Splitter,
- Product)
+ Product,
+)
from idaes.models.unit_models.mixer import MomentumMixingType
from idaes.models.unit_models.pressure_changer import ThermodynamicAssumption
from idaes.core import UnitModelCostingBlock
@@ -71,8 +76,7 @@ def build_model(m):
# Define model components and blocks
m.fs = FlowsheetBlock(dynamic=False)
- m.fs.thermo_params_VLE = GenericParameterBlock(
- **thermo_props_VLE.config_dict)
+ m.fs.thermo_params_VLE = GenericParameterBlock(**thermo_props_VLE.config_dict)
m.fs.thermo_params_VLE.set_default_scaling("flow_mol", 1)
m.fs.thermo_params_VLE.set_default_scaling("temperature", 1e-2)
@@ -80,23 +84,20 @@ def build_model(m):
m.fs.thermo_params_VLE.set_default_scaling("enth_mol", 1e-3)
m.fs.thermo_params_VLE.set_default_scaling("entr_mol", 1e-1)
for comp in thermo_props_VLE.config_dict["components"]:
- m.fs.thermo_params_VLE.set_default_scaling("mole_frac_comp",
- 1e2, index=comp)
- m.fs.thermo_params_VLE.set_default_scaling("enth_mol_comp",
- 1e2, index=comp)
- m.fs.thermo_params_VLE.set_default_scaling("enth_mol_phase",
- 1e2, index=comp)
- m.fs.thermo_params_VLE.set_default_scaling("entr_mol_phase_comp",
- 1e2, index=comp)
+ m.fs.thermo_params_VLE.set_default_scaling("mole_frac_comp", 1e2, index=comp)
+ m.fs.thermo_params_VLE.set_default_scaling("enth_mol_comp", 1e2, index=comp)
+ m.fs.thermo_params_VLE.set_default_scaling("enth_mol_phase", 1e2, index=comp)
+ m.fs.thermo_params_VLE.set_default_scaling(
+ "entr_mol_phase_comp", 1e2, index=comp
+ )
for attr in dir(getattr(m.fs.thermo_params_VLE, comp)):
- if 'coef' in attr:
- iscale.set_scaling_factor(getattr(
- getattr(m.fs.thermo_params_VLE, comp), attr), 1)
- m.fs.thermo_params_VLE.set_default_scaling("flow_mol_phase_comp",
- 1, index=comp)
+ if "coef" in attr:
+ iscale.set_scaling_factor(
+ getattr(getattr(m.fs.thermo_params_VLE, comp), attr), 1
+ )
+ m.fs.thermo_params_VLE.set_default_scaling("flow_mol_phase_comp", 1, index=comp)
- m.fs.thermo_params_vapor = GenericParameterBlock(
- **thermo_props_vapor.config_dict)
+ m.fs.thermo_params_vapor = GenericParameterBlock(**thermo_props_vapor.config_dict)
m.fs.thermo_params_vapor.set_default_scaling("flow_mol", 1)
m.fs.thermo_params_vapor.set_default_scaling("temperature", 1e-2)
@@ -104,91 +105,115 @@ def build_model(m):
m.fs.thermo_params_vapor.set_default_scaling("enth_mol", 1e-3)
m.fs.thermo_params_vapor.set_default_scaling("entr_mol", 1e-1)
for comp in thermo_props_vapor.config_dict["components"]:
- m.fs.thermo_params_vapor.set_default_scaling("mole_frac_comp",
- 1e2, index=comp)
- m.fs.thermo_params_vapor.set_default_scaling("enth_mol_comp",
- 1e2, index=comp)
- m.fs.thermo_params_vapor.set_default_scaling("enth_mol_phase",
- 1e2, index=comp)
- m.fs.thermo_params_vapor.set_default_scaling("entr_mol_phase_comp",
- 1e2, index=comp)
+ m.fs.thermo_params_vapor.set_default_scaling("mole_frac_comp", 1e2, index=comp)
+ m.fs.thermo_params_vapor.set_default_scaling("enth_mol_comp", 1e2, index=comp)
+ m.fs.thermo_params_vapor.set_default_scaling("enth_mol_phase", 1e2, index=comp)
+ m.fs.thermo_params_vapor.set_default_scaling(
+ "entr_mol_phase_comp", 1e2, index=comp
+ )
for attr in dir(getattr(m.fs.thermo_params_vapor, comp)):
- if 'coef' in attr:
- iscale.set_scaling_factor(getattr(
- getattr(m.fs.thermo_params_vapor, comp), attr), 1)
- m.fs.thermo_params_vapor.set_default_scaling("flow_mol_phase_comp",
- 1, index=comp)
+ if "coef" in attr:
+ iscale.set_scaling_factor(
+ getattr(getattr(m.fs.thermo_params_vapor, comp), attr), 1
+ )
+ m.fs.thermo_params_vapor.set_default_scaling(
+ "flow_mol_phase_comp", 1, index=comp
+ )
m.fs.reaction_params = GenericReactionParameterBlock(
- property_package=m.fs.thermo_params_vapor,
- **reaction_props.config_dict)
+ property_package=m.fs.thermo_params_vapor, **reaction_props.config_dict
+ )
# feed blocks
m.fs.H2 = Feed(property_package=m.fs.thermo_params_vapor)
m.fs.CO = Feed(property_package=m.fs.thermo_params_vapor)
# mixing feed streams
- m.fs.M101 = Mixer(property_package=m.fs.thermo_params_vapor,
- momentum_mixing_type=MomentumMixingType.minimize,
- has_phase_equilibrium=True,
- inlet_list=['H2_WGS', 'CO_WGS'])
+ m.fs.M101 = Mixer(
+ property_package=m.fs.thermo_params_vapor,
+ momentum_mixing_type=MomentumMixingType.minimize,
+ has_phase_equilibrium=True,
+ inlet_list=["H2_WGS", "CO_WGS"],
+ )
# mixing recycle back into feed streams
- m.fs.M102 = Mixer(property_package=m.fs.thermo_params_vapor,
- momentum_mixing_type=MomentumMixingType.minimize,
- has_phase_equilibrium=True,
- inlet_list=["feed", "recycle"])
+ m.fs.M102 = Mixer(
+ property_package=m.fs.thermo_params_vapor,
+ momentum_mixing_type=MomentumMixingType.minimize,
+ has_phase_equilibrium=True,
+ inlet_list=["feed", "recycle"],
+ )
# pre-compression
- m.fs.C101 = Compressor(dynamic=False,
- property_package=m.fs.thermo_params_vapor,
- compressor=True,
- thermodynamic_assumption=ThermodynamicAssumption.isothermal)
+ m.fs.C101 = Compressor(
+ dynamic=False,
+ property_package=m.fs.thermo_params_vapor,
+ compressor=True,
+ thermodynamic_assumption=ThermodynamicAssumption.isothermal,
+ )
# pre-heating
- m.fs.H101 = Heater(property_package=m.fs.thermo_params_vapor,
- has_pressure_change=False,
- has_phase_equilibrium=False)
+ m.fs.H101 = Heater(
+ property_package=m.fs.thermo_params_vapor,
+ has_pressure_change=False,
+ has_phase_equilibrium=False,
+ )
# reactor
- m.fs.R101 = StoichiometricReactor(has_heat_transfer=True,
- has_heat_of_reaction=True,
- has_pressure_change=False,
- property_package=m.fs.thermo_params_vapor,
- reaction_package=m.fs.reaction_params)
+ m.fs.R101 = StoichiometricReactor(
+ has_heat_transfer=True,
+ has_heat_of_reaction=True,
+ has_pressure_change=False,
+ property_package=m.fs.thermo_params_vapor,
+ reaction_package=m.fs.reaction_params,
+ )
# post-expansion
- m.fs.T101 = Turbine(dynamic=False,
- property_package=m.fs.thermo_params_vapor)
+ m.fs.T101 = Turbine(dynamic=False, property_package=m.fs.thermo_params_vapor)
# post-cooling
- m.fs.H102 = Heater(property_package=m.fs.thermo_params_vapor,
- has_pressure_change=False,
- has_phase_equilibrium=False)
+ m.fs.H102 = Heater(
+ property_package=m.fs.thermo_params_vapor,
+ has_pressure_change=False,
+ has_phase_equilibrium=False,
+ )
# product recovery
- m.fs.F101 = Flash(property_package=m.fs.thermo_params_VLE,
- has_heat_transfer=True,
- has_pressure_change=True)
+ m.fs.F101 = Flash(
+ property_package=m.fs.thermo_params_VLE,
+ has_heat_transfer=True,
+ has_pressure_change=True,
+ )
# split waste gases for recycle
- m.fs.S101 = Splitter(property_package=m.fs.thermo_params_vapor,
- ideal_separation=False,
- outlet_list=["purge", "recycle"])
+ m.fs.S101 = Splitter(
+ property_package=m.fs.thermo_params_vapor,
+ ideal_separation=False,
+ outlet_list=["purge", "recycle"],
+ )
# product blocks
m.fs.EXHAUST = Product(property_package=m.fs.thermo_params_vapor)
m.fs.CH3OH = Product(property_package=m.fs.thermo_params_VLE)
# Build the flowsheet
- print('Unit degrees of freedom')
- for unit in ('M101', 'C101', 'H101', 'R101', 'T101', 'H102', 'F101',
- 'M102', 'S101'):
- if unit == 'M101' or unit == 'M102':
+ print("Unit degrees of freedom")
+ for unit in (
+ "M101",
+ "C101",
+ "H101",
+ "R101",
+ "T101",
+ "H102",
+ "F101",
+ "M102",
+ "S101",
+ ):
+ if unit == "M101" or unit == "M102":
spec = 14 # (FTP + 4 mole fractions) for both feed streams
else:
spec = 7 # (FTP + 4 mole fractions) for feed stream
- print(str(unit)+' '+str(degrees_of_freedom(getattr(m.fs, unit))-spec))
+ print(str(unit) + " " + str(degrees_of_freedom(getattr(m.fs, unit)) - spec))
# feed streams
m.fs.H2_FEED = Arc(source=m.fs.H2.outlet, destination=m.fs.M101.H2_WGS)
@@ -229,184 +254,214 @@ def build_model(m):
TransformationFactory("network.expand_arcs").apply_to(m)
# Add unit and stream specifications
- print('Total DOF: ', degrees_of_freedom(m))
+ print("Total DOF: ", degrees_of_freedom(m))
return m
def set_inputs(m):
# feed streams, post WGS
- m.fs.H2.outlet.flow_mol[0].fix(637.2*pyunits.mol/pyunits.s)
- m.fs.H2.outlet.mole_frac_comp[0, "H2"].fix(1*pyunits.dimensionless)
- m.fs.H2.outlet.mole_frac_comp[0, "CO"].fix(1e-6*pyunits.dimensionless)
- m.fs.H2.outlet.mole_frac_comp[0, "CH3OH"].fix(1e-6*pyunits.dimensionless)
- m.fs.H2.outlet.mole_frac_comp[0, "CH4"].fix(1e-6*pyunits.dimensionless)
- m.fs.H2.outlet.enth_mol[0].fix(-142.4*pyunits.J/pyunits.mol)
- m.fs.H2.outlet.pressure.fix(30e5*pyunits.Pa)
-
- m.fs.CO.outlet.flow_mol[0].fix(316.8*pyunits.mol/pyunits.s)
- m.fs.CO.outlet.mole_frac_comp[0, "H2"].fix(1e-6*pyunits.dimensionless)
- m.fs.CO.outlet.mole_frac_comp[0, "CO"].fix(1*pyunits.dimensionless)
- m.fs.CO.outlet.mole_frac_comp[0, "CH3OH"].fix(1e-6*pyunits.dimensionless)
- m.fs.CO.outlet.mole_frac_comp[0, "CH4"].fix(1e-6*pyunits.dimensionless)
- m.fs.CO.outlet.enth_mol[0].fix(-110676.4*pyunits.J/pyunits.mol)
- m.fs.CO.outlet.pressure.fix(30e5*pyunits.Pa)
- print('DOF after streams specified: ', degrees_of_freedom(m))
+ m.fs.H2.outlet.flow_mol[0].fix(637.2 * pyunits.mol / pyunits.s)
+ m.fs.H2.outlet.mole_frac_comp[0, "H2"].fix(1 * pyunits.dimensionless)
+ m.fs.H2.outlet.mole_frac_comp[0, "CO"].fix(1e-6 * pyunits.dimensionless)
+ m.fs.H2.outlet.mole_frac_comp[0, "CH3OH"].fix(1e-6 * pyunits.dimensionless)
+ m.fs.H2.outlet.mole_frac_comp[0, "CH4"].fix(1e-6 * pyunits.dimensionless)
+ m.fs.H2.outlet.enth_mol[0].fix(-142.4 * pyunits.J / pyunits.mol)
+ m.fs.H2.outlet.pressure.fix(30e5 * pyunits.Pa)
+
+ m.fs.CO.outlet.flow_mol[0].fix(316.8 * pyunits.mol / pyunits.s)
+ m.fs.CO.outlet.mole_frac_comp[0, "H2"].fix(1e-6 * pyunits.dimensionless)
+ m.fs.CO.outlet.mole_frac_comp[0, "CO"].fix(1 * pyunits.dimensionless)
+ m.fs.CO.outlet.mole_frac_comp[0, "CH3OH"].fix(1e-6 * pyunits.dimensionless)
+ m.fs.CO.outlet.mole_frac_comp[0, "CH4"].fix(1e-6 * pyunits.dimensionless)
+ m.fs.CO.outlet.enth_mol[0].fix(-110676.4 * pyunits.J / pyunits.mol)
+ m.fs.CO.outlet.pressure.fix(30e5 * pyunits.Pa)
+ print("DOF after streams specified: ", degrees_of_freedom(m))
# units specifications
- m.fs.C101.outlet.pressure.fix(51e5*pyunits.Pa)
+ m.fs.C101.outlet.pressure.fix(51e5 * pyunits.Pa)
m.fs.H101.outlet_temp = Constraint(
- expr=m.fs.H101.control_volume.properties_out[0].temperature ==
- 488.15 * pyunits.K)
+ expr=m.fs.H101.control_volume.properties_out[0].temperature
+ == 488.15 * pyunits.K
+ )
m.fs.R101.conversion = Var(initialize=0.75, bounds=(0, 1))
m.fs.R101.conv_constraint = Constraint(
- expr=(m.fs.R101.conversion * m.fs.R101.inlet.flow_mol[0] *
- m.fs.R101.inlet.mole_frac_comp[0, "CO"] ==
- m.fs.R101.inlet.flow_mol[0] *
- m.fs.R101.inlet.mole_frac_comp[0, "CO"]
- - m.fs.R101.outlet.flow_mol[0] *
- m.fs.R101.outlet.mole_frac_comp[0, "CO"]))
- m.fs.R101.conversion.fix(0.75*pyunits.dimensionless)
+ expr=(
+ m.fs.R101.conversion
+ * m.fs.R101.inlet.flow_mol[0]
+ * m.fs.R101.inlet.mole_frac_comp[0, "CO"]
+ == m.fs.R101.inlet.flow_mol[0] * m.fs.R101.inlet.mole_frac_comp[0, "CO"]
+ - m.fs.R101.outlet.flow_mol[0] * m.fs.R101.outlet.mole_frac_comp[0, "CO"]
+ )
+ )
+ m.fs.R101.conversion.fix(0.75 * pyunits.dimensionless)
m.fs.R101.outlet_temp = Constraint(
- expr=m.fs.R101.control_volume.properties_out[0].temperature ==
- 507.15 * pyunits.K)
+ expr=m.fs.R101.control_volume.properties_out[0].temperature
+ == 507.15 * pyunits.K
+ )
# rxn is exothermic, so duty is cooling only
- m.fs.R101.heat_duty.setub(0*pyunits.W)
+ m.fs.R101.heat_duty.setub(0 * pyunits.W)
- m.fs.T101.deltaP.fix(-2e6*pyunits.Pa)
- m.fs.T101.efficiency_isentropic.fix(0.9*pyunits.dimensionless)
+ m.fs.T101.deltaP.fix(-2e6 * pyunits.Pa)
+ m.fs.T101.efficiency_isentropic.fix(0.9 * pyunits.dimensionless)
m.fs.H102.outlet_temp = Constraint(
- expr=m.fs.H102.control_volume.properties_out[0].temperature ==
- 407.15 * pyunits.K)
+ expr=m.fs.H102.control_volume.properties_out[0].temperature
+ == 407.15 * pyunits.K
+ )
m.fs.F101.recovery = Var(initialize=0.01, bounds=(0, 1))
m.fs.F101.rec_constraint = Constraint(
- expr=(m.fs.F101.recovery == m.fs.F101.liq_outlet.flow_mol[0] *
- m.fs.F101.liq_outlet.mole_frac_comp[0, "CH3OH"] /
- (m.fs.F101.inlet.flow_mol[0] *
- m.fs.F101.inlet.mole_frac_comp[0, "CH3OH"])))
- m.fs.F101.deltaP.fix(0*pyunits.Pa)
+ expr=(
+ m.fs.F101.recovery
+ == m.fs.F101.liq_outlet.flow_mol[0]
+ * m.fs.F101.liq_outlet.mole_frac_comp[0, "CH3OH"]
+ / (m.fs.F101.inlet.flow_mol[0] * m.fs.F101.inlet.mole_frac_comp[0, "CH3OH"])
+ )
+ )
+ m.fs.F101.deltaP.fix(0 * pyunits.Pa)
m.fs.F101.outlet_temp = Constraint(
- expr=m.fs.F101.control_volume.properties_out[0].temperature ==
- 407.15 * pyunits.K)
+ expr=m.fs.F101.control_volume.properties_out[0].temperature
+ == 407.15 * pyunits.K
+ )
m.fs.S101.split_fraction[0, "purge"].fix(0.9999) # initially no recycle
- print('DOF after units specified: ', degrees_of_freedom(m))
+ print("DOF after units specified: ", degrees_of_freedom(m))
def scale_flowsheet(m):
for var in m.fs.component_data_objects(Var, descend_into=True):
- if 'flow_mol' in var.name:
+ if "flow_mol" in var.name:
iscale.set_scaling_factor(var, 1)
- if 'temperature' in var.name:
+ if "temperature" in var.name:
iscale.set_scaling_factor(var, 1e-2)
- if 'pressure' in var.name:
+ if "pressure" in var.name:
iscale.set_scaling_factor(var, 1e-2)
- if 'enth_mol' in var.name:
+ if "enth_mol" in var.name:
iscale.set_scaling_factor(var, 1e-3)
- if 'enth_mol_phase' in var.name:
+ if "enth_mol_phase" in var.name:
iscale.set_scaling_factor(var, 1e-3)
- if 'mole_frac' in var.name:
+ if "mole_frac" in var.name:
iscale.set_scaling_factor(var, 1e2)
- if 'entr_mol' in var.name:
+ if "entr_mol" in var.name:
iscale.set_scaling_factor(var, 1e-1)
- if 'rate_reaction_extent' in var.name:
+ if "rate_reaction_extent" in var.name:
iscale.set_scaling_factor(var, 1e-3)
- if 'heat' in var.name:
+ if "heat" in var.name:
iscale.set_scaling_factor(var, 1e-3)
- if 'work' in var.name:
+ if "work" in var.name:
iscale.set_scaling_factor(var, 1e-3)
# manual scaling
- for unit in ('H2', 'CO', 'M101', 'M102', 'C101', 'H101', 'R101',
- 'T101', 'H102', 'F101', 'S101', 'CH3OH'):
+ for unit in (
+ "H2",
+ "CO",
+ "M101",
+ "M102",
+ "C101",
+ "H101",
+ "R101",
+ "T101",
+ "H102",
+ "F101",
+ "S101",
+ "CH3OH",
+ ):
block = getattr(m.fs, unit)
if hasattr(block, "control_volume"):
iscale.set_scaling_factor(
- block.control_volume.properties_in[0.0].mole_frac_comp, 1e2)
+ block.control_volume.properties_in[0.0].mole_frac_comp, 1e2
+ )
iscale.set_scaling_factor(
- block.control_volume.properties_out[0.0].mole_frac_comp, 1e2)
+ block.control_volume.properties_out[0.0].mole_frac_comp, 1e2
+ )
iscale.set_scaling_factor(
- block.control_volume.properties_in[0.0].enth_mol_phase, 1e2)
+ block.control_volume.properties_in[0.0].enth_mol_phase, 1e2
+ )
iscale.set_scaling_factor(
- block.control_volume.properties_out[0.0].enth_mol_phase, 1e2)
+ block.control_volume.properties_out[0.0].enth_mol_phase, 1e2
+ )
if hasattr(block.control_volume, "rate_reaction_extent"):
- iscale.set_scaling_factor(block.control_volume
- .rate_reaction_extent, 1e3)
+ iscale.set_scaling_factor(
+ block.control_volume.rate_reaction_extent, 1e3
+ )
if hasattr(block.control_volume, "heat"):
iscale.set_scaling_factor(block.control_volume.heat, 1e-3)
if hasattr(block.control_volume, "work"):
iscale.set_scaling_factor(block.control_volume.work, 1e-3)
if hasattr(block, "properties_isentropic"):
iscale.set_scaling_factor(
- block.properties_isentropic[0.0].mole_frac_comp, 1e2)
+ block.properties_isentropic[0.0].mole_frac_comp, 1e2
+ )
iscale.set_scaling_factor(
- block.properties_isentropic[0.0].enth_mol_phase, 1e-3)
+ block.properties_isentropic[0.0].enth_mol_phase, 1e-3
+ )
if hasattr(block, "properties"):
- iscale.set_scaling_factor(
- block.properties[0.0].mole_frac_comp, 1e2)
+ iscale.set_scaling_factor(block.properties[0.0].mole_frac_comp, 1e2)
if hasattr(block, "split"):
- iscale.set_scaling_factor(
- block.split._Liq_flow_mol_ref, 1)
- iscale.set_scaling_factor(
- block.split._Vap_flow_mol_ref, 1)
- iscale.set_scaling_factor(
- block.split._Liq_mole_frac_comp_ref, 1e2)
- iscale.set_scaling_factor(
- block.split._Vap_mole_frac_comp_ref, 1e2)
- iscale.set_scaling_factor(
- block.split._Liq_enth_mol_ref, 1e-3)
- iscale.set_scaling_factor(
- block.split._Vap_enth_mol_ref, 1e-3)
+ iscale.set_scaling_factor(block.split._Liq_flow_mol_ref, 1)
+ iscale.set_scaling_factor(block.split._Vap_flow_mol_ref, 1)
+ iscale.set_scaling_factor(block.split._Liq_mole_frac_comp_ref, 1e2)
+ iscale.set_scaling_factor(block.split._Vap_mole_frac_comp_ref, 1e2)
+ iscale.set_scaling_factor(block.split._Liq_enth_mol_ref, 1e-3)
+ iscale.set_scaling_factor(block.split._Vap_enth_mol_ref, 1e-3)
# set scaling for unit constraints
- for name in ('M101', 'M102', 'C101', 'H101', 'R101',
- 'T101', 'H102', 'F101', 'S101'):
+ for name in (
+ "M101",
+ "M102",
+ "C101",
+ "H101",
+ "R101",
+ "T101",
+ "H102",
+ "F101",
+ "S101",
+ ):
unit = getattr(m.fs, name)
# mixer constraints
- if hasattr(unit, 'material_mixing_equations'):
+ if hasattr(unit, "material_mixing_equations"):
for (t, j), c in unit.material_mixing_equations.items():
iscale.constraint_scaling_transform(c, 1, overwrite=False)
- if hasattr(unit, 'enthalpy_mixing_equations'):
+ if hasattr(unit, "enthalpy_mixing_equations"):
for t, c in unit.enthalpy_mixing_equations.items():
iscale.constraint_scaling_transform(c, 1e-3, overwrite=False)
- if hasattr(unit, 'minimum_pressure_constraint'):
+ if hasattr(unit, "minimum_pressure_constraint"):
for (t, i), c in unit.minimum_pressure_constraint.items():
iscale.constraint_scaling_transform(c, 1e-2, overwrite=False)
- if hasattr(unit, 'mixture_pressure'):
+ if hasattr(unit, "mixture_pressure"):
for t, c in unit.mixture_pressure.items():
iscale.constraint_scaling_transform(c, 1e-2, overwrite=False)
- if hasattr(unit, 'pressure_equality_constraints'):
+ if hasattr(unit, "pressure_equality_constraints"):
for (t, i), c in unit.pressure_equality_constraints.items():
iscale.constraint_scaling_transform(c, 1e-5, overwrite=False)
# splitter constraints
- if hasattr(unit, 'material_splitting_eqn'):
+ if hasattr(unit, "material_splitting_eqn"):
for (t, o, j), c in unit.material_splitting_eqn.items():
iscale.constraint_scaling_transform(c, 1, overwrite=False)
- if hasattr(unit, 'temperature_equality_eqn'):
+ if hasattr(unit, "temperature_equality_eqn"):
for (t, o), c in unit.temperature_equality_eqn.items():
iscale.constraint_scaling_transform(c, 1e-2, overwrite=False)
- if hasattr(unit, 'molar_enthalpy_equality_eqn'):
+ if hasattr(unit, "molar_enthalpy_equality_eqn"):
for (t, o), c in unit.molar_enthalpy_equality_eqn.items():
iscale.constraint_scaling_transform(c, 1e-3, overwrite=False)
- if hasattr(unit, 'molar_enthalpy_splitting_eqn'):
+ if hasattr(unit, "molar_enthalpy_splitting_eqn"):
for (t, o), c in unit.molar_enthalpy_splitting_eqn.items():
iscale.constraint_scaling_transform(c, 1e-3, overwrite=False)
- if hasattr(unit, 'pressure_equality_eqn'):
+ if hasattr(unit, "pressure_equality_eqn"):
for (t, o), c in unit.pressure_equality_eqn.items():
iscale.constraint_scaling_transform(c, 1e-5, overwrite=False)
- if hasattr(unit, 'sum_split_frac'):
+ if hasattr(unit, "sum_split_frac"):
for t, c in unit.sum_split_frac.items():
iscale.constraint_scaling_transform(c, 1e2, overwrite=False)
# flash adds same as splitter, plus one more
- if hasattr(unit, 'split_fraction_eq'):
+ if hasattr(unit, "split_fraction_eq"):
for (t, o), c in unit.split_fraction_eq.items():
iscale.constraint_scaling_transform(c, 1e2, overwrite=False)
@@ -449,41 +504,41 @@ def scale_flowsheet(m):
iscale.constraint_scaling_transform(c, 1e-2, overwrite=False)
# heater and reactor only add 0D control volume constraints
- if hasattr(unit, 'material_holdup_calculation'):
+ if hasattr(unit, "material_holdup_calculation"):
for (t, p, j), c in unit.material_holdup_calculation.items():
iscale.constraint_scaling_transform(c, 1, overwrite=False)
- if hasattr(unit, 'rate_reaction_stoichiometry_constraint'):
- for (t, p, j), c in (
- unit.rate_reaction_stoichiometry_constraint.items()):
+ if hasattr(unit, "rate_reaction_stoichiometry_constraint"):
+ for (t, p, j), c in unit.rate_reaction_stoichiometry_constraint.items():
iscale.constraint_scaling_transform(c, 1, overwrite=False)
- if hasattr(unit, 'equilibrium_reaction_stoichiometry_constraint'):
- for (t, p, j), c in (
- unit.equilibrium_reaction_stoichiometry_constraint
- .items()):
+ if hasattr(unit, "equilibrium_reaction_stoichiometry_constraint"):
+ for (
+ t,
+ p,
+ j,
+ ), c in unit.equilibrium_reaction_stoichiometry_constraint.items():
iscale.constraint_scaling_transform(c, 1, overwrite=False)
- if hasattr(unit, 'inherent_reaction_stoichiometry_constraint'):
- for (t, p, j), c in (
- unit.inherent_reaction_stoichiometry_constraint.items()):
+ if hasattr(unit, "inherent_reaction_stoichiometry_constraint"):
+ for (t, p, j), c in unit.inherent_reaction_stoichiometry_constraint.items():
iscale.constraint_scaling_transform(c, 1, overwrite=False)
- if hasattr(unit, 'material_balances'):
+ if hasattr(unit, "material_balances"):
for (t, p, j), c in unit.material_balances.items():
iscale.constraint_scaling_transform(c, 1, overwrite=False)
- if hasattr(unit, 'element_balances'):
+ if hasattr(unit, "element_balances"):
for (t, e), c in unit.element_balances.items():
iscale.constraint_scaling_transform(c, 1, overwrite=False)
- if hasattr(unit, 'elemental_holdup_calculation'):
+ if hasattr(unit, "elemental_holdup_calculation"):
for (t, e), c in unit.elemental_holdup_calculation.items():
iscale.constraint_scaling_transform(c, 1, overwrite=False)
- if hasattr(unit, 'enthalpy_balances'):
+ if hasattr(unit, "enthalpy_balances"):
for t, c in unit.enthalpy_balances.items():
iscale.constraint_scaling_transform(c, 1e-3, overwrite=False)
- if hasattr(unit, 'energy_holdup_calculation'):
+ if hasattr(unit, "energy_holdup_calculation"):
for (t, p), c in unit.energy_holdup_calculation.items():
iscale.constraint_scaling_transform(c, 1e-3, overwrite=False)
- if hasattr(unit, 'pressure_balance'):
+ if hasattr(unit, "pressure_balance"):
for t, c in unit.pressure_balance.items():
iscale.constraint_scaling_transform(c, 1e-5, overwrite=False)
- if hasattr(unit, 'sum_of_phase_fractions'):
+ if hasattr(unit, "sum_of_phase_fractions"):
for t, c in unit.sum_of_phase_fractions.items():
iscale.constraint_scaling_transform(c, 1e2, overwrite=False)
if hasattr(unit, "material_accumulation_disc_eq"):
@@ -503,33 +558,23 @@ def scale_flowsheet(m):
for c in arc.component_data_objects(Constraint, descend_into=True):
if hasattr(unit, "enth_mol_equality"):
for t, c in unit.enth_mol_equality.items():
- iscale.constraint_scaling_transform(c, 1e-3,
- overwrite=False)
+ iscale.constraint_scaling_transform(c, 1e-3, overwrite=False)
if hasattr(unit, "flow_mol_equality"):
for t, c in unit.flow_mol_equality.items():
- iscale.constraint_scaling_transform(c, 1,
- overwrite=False)
+ iscale.constraint_scaling_transform(c, 1, overwrite=False)
if hasattr(unit, "mole_frac_comp_equality"):
for (t, j), c in unit.mole_frac_comp_equality.items():
- iscale.constraint_scaling_transform(c, 1e2,
- overwrite=False)
+ iscale.constraint_scaling_transform(c, 1e2, overwrite=False)
if hasattr(unit, "pressure_equality"):
for t, c in unit.pressure_equality.items():
- iscale.constraint_scaling_transform(c, 1e-5,
- overwrite=False)
-
- iscale.set_scaling_factor(
- m.fs.M101.H2_WGS_state[0.0].enth_mol_phase['Vap'], 1e-3)
- iscale.set_scaling_factor(
- m.fs.M101.CO_WGS_state[0.0].enth_mol_phase['Vap'], 1e-3)
- iscale.set_scaling_factor(
- m.fs.M101.mixed_state[0.0].enth_mol_phase['Vap'], 1e-3)
- iscale.set_scaling_factor(
- m.fs.M102.feed_state[0.0].enth_mol_phase['Vap'], 1e-3)
- iscale.set_scaling_factor(
- m.fs.M102.recycle_state[0.0].enth_mol_phase['Vap'], 1e-3)
- iscale.set_scaling_factor(
- m.fs.M102.mixed_state[0.0].enth_mol_phase['Vap'], 1e-3)
+ iscale.constraint_scaling_transform(c, 1e-5, overwrite=False)
+
+ iscale.set_scaling_factor(m.fs.M101.H2_WGS_state[0.0].enth_mol_phase["Vap"], 1e-3)
+ iscale.set_scaling_factor(m.fs.M101.CO_WGS_state[0.0].enth_mol_phase["Vap"], 1e-3)
+ iscale.set_scaling_factor(m.fs.M101.mixed_state[0.0].enth_mol_phase["Vap"], 1e-3)
+ iscale.set_scaling_factor(m.fs.M102.feed_state[0.0].enth_mol_phase["Vap"], 1e-3)
+ iscale.set_scaling_factor(m.fs.M102.recycle_state[0.0].enth_mol_phase["Vap"], 1e-3)
+ iscale.set_scaling_factor(m.fs.M102.mixed_state[0.0].enth_mol_phase["Vap"], 1e-3)
iscale.calculate_scaling_factors(m)
@@ -548,11 +593,11 @@ def initialize_flowsheet(m):
heuristic_tear_set = seq.tear_set_arcs(G, method="heuristic")
order = seq.calculation_order(G)
print()
- print('Tear Stream:')
+ print("Tear Stream:")
for o in heuristic_tear_set:
- print(o.name, ': ', o.source.name, ' to ', o.destination.name)
+ print(o.name, ": ", o.source.name, " to ", o.destination.name)
print()
- print('Calculation order:')
+ print("Calculation order:")
for o in order:
print(o[0].name)
print()
@@ -560,12 +605,14 @@ def initialize_flowsheet(m):
tear_guesses = {
"flow_mol": {0: 954.00},
"mole_frac_comp": {
- (0, "CH4"): 1e-6,
- (0, "CO"): 0.33207,
- (0, "H2"): 0.66792,
- (0, "CH3OH"): 1e-6},
+ (0, "CH4"): 1e-6,
+ (0, "CO"): 0.33207,
+ (0, "H2"): 0.66792,
+ (0, "CH3OH"): 1e-6,
+ },
"enth_mol": {0: -36848},
- "pressure": {0: 3e6}}
+ "pressure": {0: 3e6},
+ }
# automatically build stream set for flowsheet and find the tear stream
stream_set = [arc for arc in m.fs.component_data_objects(Arc)]
@@ -574,20 +621,21 @@ def initialize_flowsheet(m):
seq.set_guesses_for(stream.destination, tear_guesses)
def function(unit):
- print('Solving ', str(unit))
+ print("Solving ", str(unit))
unit.initialize(outlvl=idaeslog.ERROR) # no output unless it breaks
for stream in stream_set:
if stream.source.parent_block() == unit:
propagate_state(arc=stream) # this is an outlet of the unit
stream.destination.unfix()
- print('DOF = ', degrees_of_freedom(m))
- print('Initial DOF = ', degrees_of_freedom(m))
+ print("DOF = ", degrees_of_freedom(m))
+
+ print("Initial DOF = ", degrees_of_freedom(m))
seq.run(m, function)
for stream in stream_set:
if stream.destination.is_fixed() is True:
- print('Unfixing ', stream.destination.name, '...')
+ print("Unfixing ", stream.destination.name, "...")
stream.destination.unfix()
- print('Final DOF = ', degrees_of_freedom(m))
+ print("Final DOF = ", degrees_of_freedom(m))
def add_costing(m):
@@ -600,88 +648,110 @@ def add_costing(m):
m.fs.cooling_cost = Expression(
expr=0.25e-7 * (-m.fs.F101.heat_duty[0])
+ 0.212e-7 * (-m.fs.H102.heat_duty[0])
- + 2.2e-7 * (-m.fs.R101.heat_duty[0]))
+ + 2.2e-7 * (-m.fs.R101.heat_duty[0])
+ )
# Expression to compute the total heating cost (F/R heating not assumed)
- m.fs.heating_cost = Expression(
- expr=2.2e-7 * m.fs.H101.heat_duty[0])
+ m.fs.heating_cost = Expression(expr=2.2e-7 * m.fs.H101.heat_duty[0])
# Expression to compute the total electricity cost (utilities - credit)
m.fs.electricity_cost = Expression(
expr=0.12e-5 * (m.fs.C101.work_mechanical[0])
- - 0.08e-5 * (m.fs.T101.work_isentropic[0]))
+ - 0.08e-5 * (m.fs.T101.work_isentropic[0])
+ )
# Expression to compute the total operating cost
m.fs.operating_cost = Expression(
- expr=(3600 * 24 * 365 * (m.fs.heating_cost + m.fs.cooling_cost
- + m.fs.electricity_cost)))
+ expr=(
+ 3600
+ * 24
+ * 365
+ * (m.fs.heating_cost + m.fs.cooling_cost + m.fs.electricity_cost)
+ )
+ )
# Computing reactor capital cost
m.fs.R101.diameter = Param(initialize=2, units=pyunits.m)
m.fs.R101.length = Var(initialize=4, units=pyunits.m)
m.fs.R101.diameter = 2 * pyunits.m
- m.fs.R101.length.fix(4*pyunits.m) # for initial problem at 75% conversion
- m.fs.R101.costing = UnitModelCostingBlock(
- flowsheet_costing_block=m.fs.costing)
+ m.fs.R101.length.fix(4 * pyunits.m) # for initial problem at 75% conversion
+ m.fs.R101.costing = UnitModelCostingBlock(flowsheet_costing_block=m.fs.costing)
# Reactor length (size, and capital cost) is adjusted based on conversion
# surrogate model which scales length linearly with conversion
m.fs.R101.length.unfix()
- m.fs.R101.L_eq = Constraint(expr=m.fs.R101.length ==
- 13.2000*pyunits.m*m.fs.R101.conversion -
- 5.9200*pyunits.m)
+ m.fs.R101.L_eq = Constraint(
+ expr=m.fs.R101.length
+ == 13.2000 * pyunits.m * m.fs.R101.conversion - 5.9200 * pyunits.m
+ )
# Computing flash capital cost
m.fs.F101.diameter = Param(initialize=2, units=pyunits.m)
m.fs.F101.length = Param(initialize=4, units=pyunits.m)
m.fs.F101.diameter = 2 * pyunits.m
m.fs.F101.length = 4 * pyunits.m
- m.fs.F101.costing = UnitModelCostingBlock(
- flowsheet_costing_block=m.fs.costing)
+ m.fs.F101.costing = UnitModelCostingBlock(flowsheet_costing_block=m.fs.costing)
# Computing heater/cooler capital costs
# Surrogates prepared with IDAES shell and tube hx considering IP steam and
# assuming steam outlet is condensed
m.fs.H101.cost_heater = Expression(
- expr=0.036158*m.fs.H101.heat_duty[0] + 63931.475*pyunits.W,
- doc='capital cost of heater in $')
+ expr=0.036158 * m.fs.H101.heat_duty[0] + 63931.475 * pyunits.W,
+ doc="capital cost of heater in $",
+ )
# Surrogates prepared with IDAES shell and tube hx considering cooling
# water assuming that water inlet T is 25 deg C and outlet T is 40 deg C
m.fs.H102.cost_heater = Expression(
- expr=0.10230*(-m.fs.H102.heat_duty[0]) + 100421.572*pyunits.W,
- doc='capital cost of cooler in $')
+ expr=0.10230 * (-m.fs.H102.heat_duty[0]) + 100421.572 * pyunits.W,
+ doc="capital cost of cooler in $",
+ )
# Annualizing capital cost to same scale as operating costs (per year)
m.fs.annualized_capital_cost = Expression(
- expr=(m.fs.R101.costing.capital_cost
- + m.fs.F101.costing.capital_cost
- + m.fs.H101.cost_heater
- + m.fs.H102.cost_heater)*5.4/15)
+ expr=(
+ m.fs.R101.costing.capital_cost
+ + m.fs.F101.costing.capital_cost
+ + m.fs.H101.cost_heater
+ + m.fs.H102.cost_heater
+ )
+ * 5.4
+ / 15
+ )
# methanol price $449 us dollars per metric ton - 32.042 g/mol
# - 1 gr = 1e-6 MT -- consider 1000
# H2 $16.51 per kilogram - 2.016 g/mol
# CO $62.00 per kilogram - 28.01 g/mol
m.fs.sales = Expression(
- expr=(3600 * 24 * 365
- * m.fs.CH3OH.inlet.flow_mol[0]
- * m.fs.CH3OH.inlet.mole_frac_comp[0, "CH3OH"]
- * 32.042 * 1e-6 * 449 * 1000
- )
+ expr=(
+ 3600
+ * 24
+ * 365
+ * m.fs.CH3OH.inlet.flow_mol[0]
+ * m.fs.CH3OH.inlet.mole_frac_comp[0, "CH3OH"]
+ * 32.042
+ * 1e-6
+ * 449
+ * 1000
)
+ )
m.fs.raw_mat_cost = Expression(
- expr=(3600 * 24 * 365 * m.fs.CO.outlet.flow_mol[0] *
- 16.51 * 2.016 / 1000
- + 3600 * 24 * 365 * m.fs.H2.outlet.flow_mol[0] *
- 62.00 * 28.01 / 1000
- )
+ expr=(
+ 3600 * 24 * 365 * m.fs.CO.outlet.flow_mol[0] * 16.51 * 2.016 / 1000
+ + 3600 * 24 * 365 * m.fs.H2.outlet.flow_mol[0] * 62.00 * 28.01 / 1000
)
-
- m.fs.objective = Objective(expr=(m.fs.sales
- - m.fs.operating_cost
- - m.fs.annualized_capital_cost
- - m.fs.raw_mat_cost)/1e3,
- sense=maximize)
+ )
+
+ m.fs.objective = Objective(
+ expr=(
+ m.fs.sales
+ - m.fs.operating_cost
+ - m.fs.annualized_capital_cost
+ - m.fs.raw_mat_cost
+ )
+ / 1e3,
+ sense=maximize,
+ )
assert degrees_of_freedom(m) == 0
@@ -692,61 +762,82 @@ def report(m):
print()
print()
extent = m.fs.R101.rate_reaction_extent[0, "R1"] # shorter parameter alias
- print('Extent of reaction: ', value(extent))
- print('Stoichiometry of each component normalized by the extent:')
- complist = ('CH4', 'H2', 'CH3OH', 'CO')
- changelist = [value(m.fs.R101.outlet.mole_frac_comp[0, comp] *
- m.fs.R101.outlet.flow_mol[0] -
- m.fs.R101.inlet.mole_frac_comp[0, comp] *
- m.fs.R101.inlet.flow_mol[0]) for comp in complist]
- normlist = [changelist[i]/value(extent) for i in range(len(complist))]
+ print("Extent of reaction: ", value(extent))
+ print("Stoichiometry of each component normalized by the extent:")
+ complist = ("CH4", "H2", "CH3OH", "CO")
+ changelist = [
+ value(
+ m.fs.R101.outlet.mole_frac_comp[0, comp] * m.fs.R101.outlet.flow_mol[0]
+ - m.fs.R101.inlet.mole_frac_comp[0, comp] * m.fs.R101.inlet.flow_mol[0]
+ )
+ for comp in complist
+ ]
+ normlist = [changelist[i] / value(extent) for i in range(len(complist))]
change = dict(zip(complist, normlist))
for entry in change:
- print(entry, ': ', round(change[entry], 2))
- print('These coefficients should follow 1*CO + 2*H2 => 1*CH3OH')
+ print(entry, ": ", round(change[entry], 2))
+ print("These coefficients should follow 1*CO + 2*H2 => 1*CH3OH")
print()
- print('Reaction conversion: ', m.fs.R101.conversion.value)
- print('Reactor duty (MW): ', m.fs.R101.heat_duty[0].value/1e6)
- print('Duty from Reaction (MW)):', value(extent) *
- -m.fs.reaction_params.reaction_R1.dh_rxn_ref.value/1e6)
- print('Compressor work (MW): ', m.fs.C101.work_mechanical[0].value/1e6)
- print('Turbine work (MW): ', m.fs.T101.work_isentropic[0].value/1e6)
- print('Feed Mixer outlet temperature (C)): ',
- m.fs.M101.mixed_state[0].temperature.value - 273.15)
- print('Recycle Mixer outlet temperature (C)): ',
- m.fs.M102.mixed_state[0].temperature.value - 273.15)
- print('Feed Compressor outlet temperature (C)): ',
- m.fs.C101.control_volume.properties_out[0].temperature.value
- - 273.15)
- print('Feed Compressor outlet pressure (Pa)): ',
- m.fs.C101.control_volume.properties_out[0].pressure.value)
- print('Heater outlet temperature (C)): ',
- m.fs.H101.control_volume.properties_out[0].temperature.value
- - 273.15)
- print('Reactor outlet temperature (C)): ',
- m.fs.R101.control_volume.properties_out[0].temperature.value
- - 273.15)
- print('Turbine outlet temperature (C)): ',
- m.fs.T101.control_volume.properties_out[0].temperature.value
- - 273.15)
- print('Turbine outlet pressure (Pa)): ',
- m.fs.T101.control_volume.properties_out[0].pressure.value)
- print('Cooler outlet temperature (C)): ',
- m.fs.H102.control_volume.properties_out[0].temperature.value
- - 273.15)
- print('Flash outlet temperature (C)): ',
- m.fs.F101.control_volume.properties_out[0].temperature.value
- - 273.15)
- print('Purge percentage (amount of vapor vented to exhaust):',
- 100*value(m.fs.S101.split_fraction[0, "purge"]), ' %')
- print('Methanol recovery(%): ', value(100*m.fs.F101.recovery))
- print('annualized capital cost ($/year) =',
- value(m.fs.annualized_capital_cost))
- print('operating cost ($/year) = ', value(m.fs.operating_cost))
- print('sales ($/year) = ', value(m.fs.sales))
- print('raw materials cost ($/year) =', value(m.fs.raw_mat_cost))
- print('revenue (1000$/year)= ', value(m.fs.objective))
+ print("Reaction conversion: ", m.fs.R101.conversion.value)
+ print("Reactor duty (MW): ", m.fs.R101.heat_duty[0].value / 1e6)
+ print(
+ "Duty from Reaction (MW)):",
+ value(extent) * -m.fs.reaction_params.reaction_R1.dh_rxn_ref.value / 1e6,
+ )
+ print("Compressor work (MW): ", m.fs.C101.work_mechanical[0].value / 1e6)
+ print("Turbine work (MW): ", m.fs.T101.work_isentropic[0].value / 1e6)
+ print(
+ "Feed Mixer outlet temperature (C)): ",
+ m.fs.M101.mixed_state[0].temperature.value - 273.15,
+ )
+ print(
+ "Recycle Mixer outlet temperature (C)): ",
+ m.fs.M102.mixed_state[0].temperature.value - 273.15,
+ )
+ print(
+ "Feed Compressor outlet temperature (C)): ",
+ m.fs.C101.control_volume.properties_out[0].temperature.value - 273.15,
+ )
+ print(
+ "Feed Compressor outlet pressure (Pa)): ",
+ m.fs.C101.control_volume.properties_out[0].pressure.value,
+ )
+ print(
+ "Heater outlet temperature (C)): ",
+ m.fs.H101.control_volume.properties_out[0].temperature.value - 273.15,
+ )
+ print(
+ "Reactor outlet temperature (C)): ",
+ m.fs.R101.control_volume.properties_out[0].temperature.value - 273.15,
+ )
+ print(
+ "Turbine outlet temperature (C)): ",
+ m.fs.T101.control_volume.properties_out[0].temperature.value - 273.15,
+ )
+ print(
+ "Turbine outlet pressure (Pa)): ",
+ m.fs.T101.control_volume.properties_out[0].pressure.value,
+ )
+ print(
+ "Cooler outlet temperature (C)): ",
+ m.fs.H102.control_volume.properties_out[0].temperature.value - 273.15,
+ )
+ print(
+ "Flash outlet temperature (C)): ",
+ m.fs.F101.control_volume.properties_out[0].temperature.value - 273.15,
+ )
+ print(
+ "Purge percentage (amount of vapor vented to exhaust):",
+ 100 * value(m.fs.S101.split_fraction[0, "purge"]),
+ " %",
+ )
+ print("Methanol recovery(%): ", value(100 * m.fs.F101.recovery))
+ print("annualized capital cost ($/year) =", value(m.fs.annualized_capital_cost))
+ print("operating cost ($/year) = ", value(m.fs.operating_cost))
+ print("sales ($/year) = ", value(m.fs.sales))
+ print("raw materials cost ($/year) =", value(m.fs.raw_mat_cost))
+ print("revenue (1000$/year)= ", value(m.fs.objective))
print()
m.fs.H2.report()
@@ -757,25 +848,23 @@ def report(m):
def main(m):
solver = get_solver() # IPOPT
- optarg = {'tol': 1e-6,
- 'max_iter': 500}
+ optarg = {"tol": 1e-6, "max_iter": 500}
solver.options = optarg
build_model(m) # build flowsheet
set_inputs(m) # unit and stream specifications
scale_flowsheet(m)
initialize_flowsheet(m) # rigorous initialization scheme
- print('DOF before solve: ', degrees_of_freedom(m))
+ print("DOF before solve: ", degrees_of_freedom(m))
print()
- print('Solving initial problem...')
+ print("Solving initial problem...")
results = solver.solve(m, tee=True)
assert results.solver.termination_condition == TerminationCondition.optimal
add_costing(m) # re-solve with costing equations
print()
results2 = solver.solve(m, tee=True)
- assert results2.solver.termination_condition == \
- TerminationCondition.optimal
- print('Initial solution process results:')
+ assert results2.solver.termination_condition == TerminationCondition.optimal
+ print("Initial solution process results:")
report(m) # display initial solution results
# Set up Optimization Problem (Maximize Revenue)
@@ -785,45 +874,51 @@ def main(m):
m.fs.R101.conversion_ub = Constraint(expr=m.fs.R101.conversion <= 0.85)
m.fs.R101.outlet_temp.deactivate()
m.fs.R101.outlet_t_lb = Constraint(
- expr=m.fs.R101.control_volume.properties_out[0.0].temperature
- >= 405*pyunits.K)
+ expr=m.fs.R101.control_volume.properties_out[0.0].temperature >= 405 * pyunits.K
+ )
m.fs.R101.outlet_t_ub = Constraint(
- expr=m.fs.R101.control_volume.properties_out[0.0].temperature
- <= 505*pyunits.K)
+ expr=m.fs.R101.control_volume.properties_out[0.0].temperature <= 505 * pyunits.K
+ )
# Optimize turbine work (or delta P)
m.fs.T101.deltaP.unfix() # optimize turbine work recovery/pressure drop
m.fs.T101.outlet_p_lb = Constraint(
- expr=m.fs.T101.outlet.pressure[0] >= 10E5*pyunits.Pa)
+ expr=m.fs.T101.outlet.pressure[0] >= 10e5 * pyunits.Pa
+ )
m.fs.T101.outlet_p_ub = Constraint(
- expr=m.fs.T101.outlet.pressure[0] <= 51E5*0.8*pyunits.Pa)
+ expr=m.fs.T101.outlet.pressure[0] <= 51e5 * 0.8 * pyunits.Pa
+ )
# Optimize Cooler outlet temperature - unfix cooler outlet temperature
m.fs.H102.outlet_temp.deactivate()
m.fs.H102.outlet_t_lb = Constraint(
expr=m.fs.H102.control_volume.properties_out[0.0].temperature
- >= 407.15*0.8*pyunits.K)
+ >= 407.15 * 0.8 * pyunits.K
+ )
m.fs.H102.outlet_t_ub = Constraint(
- expr=m.fs.H102.control_volume.properties_out[0.0].temperature
- <= 480*pyunits.K)
+ expr=m.fs.H102.control_volume.properties_out[0.0].temperature <= 480 * pyunits.K
+ )
m.fs.F101.deltaP.unfix() # allow pressure change in streams
m.fs.F101.isothermal = Constraint(
- expr=m.fs.F101.control_volume.properties_out[0].temperature ==
- m.fs.F101.control_volume.properties_in[0].temperature)
+ expr=m.fs.F101.control_volume.properties_out[0].temperature
+ == m.fs.F101.control_volume.properties_in[0].temperature
+ )
m.fs.S101.split_fraction[0, "purge"].unfix() # allow some gas recycle
m.fs.S101.split_fraction_lb = Constraint(
- expr=m.fs.S101.split_fraction[0, "purge"] >= 0.10) # min 10% purge
+ expr=m.fs.S101.split_fraction[0, "purge"] >= 0.10
+ ) # min 10% purge
m.fs.S101.split_fraction_ub = Constraint(
- expr=m.fs.S101.split_fraction[0, "purge"] <= 0.50) # max 50% purge
+ expr=m.fs.S101.split_fraction[0, "purge"] <= 0.50
+ ) # max 50% purge
print()
- print('Solving optimization problem...')
+ print("Solving optimization problem...")
opt_res = solver.solve(m, tee=True)
assert opt_res.solver.termination_condition == TerminationCondition.optimal
- print('Optimal solution process results:')
+ print("Optimal solution process results:")
report(m)
return m
diff --git a/idaes_examples/notebooks/docs/flowsheets/methanol_synthesis.ipynb b/idaes_examples/notebooks/docs/flowsheets/methanol_synthesis.ipynb
index 24e4ccb3..44462419 100644
--- a/idaes_examples/notebooks/docs/flowsheets/methanol_synthesis.ipynb
+++ b/idaes_examples/notebooks/docs/flowsheets/methanol_synthesis.ipynb
@@ -51,7 +51,7 @@
"source": [
"## 1. Introduction\n",
"\n",
- "This example demonstrates a simulation of methanol synthesis from hydrogen and carbon monoxide. Each methanol flowsheet module includes several built-in methods. This notebook demonstrates building the flowsheet, implementing model scaling, initialization and solving a square problem, costing and final constrainted optimization.\n",
+ "This example demonstrates a simulation of methanol synthesis from hydrogen and carbon monoxide. Each methanol flowsheet module includes several built-in methods. This notebook demonstrates building the flowsheet, implementing model scaling, initialization and solving a square problem, costing and final constrained optimization.\n",
"\n",
"The ```build_model()``` method creates the Pyomo concrete model and builds the flowsheet by importing thermophysical and reaction properties and unit models and defining stream connections between these units. This method also implements appropriate default scaling on state and property variables.\n",
"\n",
@@ -357,7 +357,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "As expected, the process achieves a much greater revenue as a result of increasing conversion and lowering the inlet temperature to the Flash unit to encourage methanol recovery in the liquid phase. The results show a slight increase in equipment and operating costs from these changes, as well as a small loss of methanol in the exhuast."
+ "As expected, the process achieves a much greater revenue as a result of increasing conversion and lowering the inlet temperature to the Flash unit to encourage methanol recovery in the liquid phase. The results show a slight increase in equipment and operating costs from these changes, as well as a small loss of methanol in the exhaust."
]
},
{
@@ -556,7 +556,7 @@
"metadata": {},
"source": [
"## 5.3 Flowsheet Costing and Optimization\n",
- "Now that we have a well-initialized and solved flowsheet, we can add process economics and optimize the revenue. We utilize IDAES costing tools to calculate reactor and flash vessel capital cost, and implement surrogate models to account for heat exchanger capital costs, reactor operating costs and utility costs for heating, cooling and electricity. As before, revenue is determined from total liquid methanol sales, operating costs, annualized capital costs and feed raw material costs. The flowsheet report method returns key process results, which are updated for new results with the prescence of a recycle stream:"
+ "Now that we have a well-initialized and solved flowsheet, we can add process economics and optimize the revenue. We utilize IDAES costing tools to calculate reactor and flash vessel capital cost, and implement surrogate models to account for heat exchanger capital costs, reactor operating costs and utility costs for heating, cooling and electricity. As before, revenue is determined from total liquid methanol sales, operating costs, annualized capital costs and feed raw material costs. The flowsheet report method returns key process results, which are updated for new results with the presence of a recycle stream:"
]
},
{
diff --git a/idaes_examples/notebooks/docs/flowsheets/methanol_synthesis_doc.ipynb b/idaes_examples/notebooks/docs/flowsheets/methanol_synthesis_doc.ipynb
index 0c126bb1..6e83e8ed 100644
--- a/idaes_examples/notebooks/docs/flowsheets/methanol_synthesis_doc.ipynb
+++ b/idaes_examples/notebooks/docs/flowsheets/methanol_synthesis_doc.ipynb
@@ -51,7 +51,7 @@
"source": [
"## 1. Introduction\n",
"\n",
- "This example demonstrates a simulation of methanol synthesis from hydrogen and carbon monoxide. Each methanol flowsheet module includes several built-in methods. This notebook demonstrates building the flowsheet, implementing model scaling, initialization and solving a square problem, costing and final constrainted optimization.\n",
+ "This example demonstrates a simulation of methanol synthesis from hydrogen and carbon monoxide. Each methanol flowsheet module includes several built-in methods. This notebook demonstrates building the flowsheet, implementing model scaling, initialization and solving a square problem, costing and final constrained optimization.\n",
"\n",
"The ```build_model()``` method creates the Pyomo concrete model and builds the flowsheet by importing thermophysical and reaction properties and unit models and defining stream connections between these units. This method also implements appropriate default scaling on state and property variables.\n",
"\n",
@@ -2164,7 +2164,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "As expected, the process achieves a much greater revenue as a result of increasing conversion and lowering the inlet temperature to the Flash unit to encourage methanol recovery in the liquid phase. The results show a slight increase in equipment and operating costs from these changes, as well as a small loss of methanol in the exhuast."
+ "As expected, the process achieves a much greater revenue as a result of increasing conversion and lowering the inlet temperature to the Flash unit to encourage methanol recovery in the liquid phase. The results show a slight increase in equipment and operating costs from these changes, as well as a small loss of methanol in the exhaust."
]
},
{
@@ -2880,7 +2880,7 @@
"metadata": {},
"source": [
"## 5.3 Flowsheet Costing and Optimization\n",
- "Now that we have a well-initialized and solved flowsheet, we can add process economics and optimize the revenue. We utilize IDAES costing tools to calculate reactor and flash vessel capital cost, and implement surrogate models to account for heat exchanger capital costs, reactor operating costs and utility costs for heating, cooling and electricity. As before, revenue is determined from total liquid methanol sales, operating costs, annualized capital costs and feed raw material costs. The flowsheet report method returns key process results, which are updated for new results with the prescence of a recycle stream:"
+ "Now that we have a well-initialized and solved flowsheet, we can add process economics and optimize the revenue. We utilize IDAES costing tools to calculate reactor and flash vessel capital cost, and implement surrogate models to account for heat exchanger capital costs, reactor operating costs and utility costs for heating, cooling and electricity. As before, revenue is determined from total liquid methanol sales, operating costs, annualized capital costs and feed raw material costs. The flowsheet report method returns key process results, which are updated for new results with the presence of a recycle stream:"
]
},
{
@@ -4123,4 +4123,4 @@
},
"nbformat": 4,
"nbformat_minor": 3
-}
\ No newline at end of file
+}
diff --git a/idaes_examples/notebooks/docs/flowsheets/methanol_synthesis_test.ipynb b/idaes_examples/notebooks/docs/flowsheets/methanol_synthesis_test.ipynb
index da4c6fec..a7ec0962 100644
--- a/idaes_examples/notebooks/docs/flowsheets/methanol_synthesis_test.ipynb
+++ b/idaes_examples/notebooks/docs/flowsheets/methanol_synthesis_test.ipynb
@@ -51,7 +51,7 @@
"source": [
"## 1. Introduction\n",
"\n",
- "This example demonstrates a simulation of methanol synthesis from hydrogen and carbon monoxide. Each methanol flowsheet module includes several built-in methods. This notebook demonstrates building the flowsheet, implementing model scaling, initialization and solving a square problem, costing and final constrainted optimization.\n",
+ "This example demonstrates a simulation of methanol synthesis from hydrogen and carbon monoxide. Each methanol flowsheet module includes several built-in methods. This notebook demonstrates building the flowsheet, implementing model scaling, initialization and solving a square problem, costing and final constrained optimization.\n",
"\n",
"The ```build_model()``` method creates the Pyomo concrete model and builds the flowsheet by importing thermophysical and reaction properties and unit models and defining stream connections between these units. This method also implements appropriate default scaling on state and property variables.\n",
"\n",
@@ -357,7 +357,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "As expected, the process achieves a much greater revenue as a result of increasing conversion and lowering the inlet temperature to the Flash unit to encourage methanol recovery in the liquid phase. The results show a slight increase in equipment and operating costs from these changes, as well as a small loss of methanol in the exhuast."
+ "As expected, the process achieves a much greater revenue as a result of increasing conversion and lowering the inlet temperature to the Flash unit to encourage methanol recovery in the liquid phase. The results show a slight increase in equipment and operating costs from these changes, as well as a small loss of methanol in the exhaust."
]
},
{
@@ -556,7 +556,7 @@
"metadata": {},
"source": [
"## 5.3 Flowsheet Costing and Optimization\n",
- "Now that we have a well-initialized and solved flowsheet, we can add process economics and optimize the revenue. We utilize IDAES costing tools to calculate reactor and flash vessel capital cost, and implement surrogate models to account for heat exchanger capital costs, reactor operating costs and utility costs for heating, cooling and electricity. As before, revenue is determined from total liquid methanol sales, operating costs, annualized capital costs and feed raw material costs. The flowsheet report method returns key process results, which are updated for new results with the prescence of a recycle stream:"
+ "Now that we have a well-initialized and solved flowsheet, we can add process economics and optimize the revenue. We utilize IDAES costing tools to calculate reactor and flash vessel capital cost, and implement surrogate models to account for heat exchanger capital costs, reactor operating costs and utility costs for heating, cooling and electricity. As before, revenue is determined from total liquid methanol sales, operating costs, annualized capital costs and feed raw material costs. The flowsheet report method returns key process results, which are updated for new results with the presence of a recycle stream:"
]
},
{
@@ -730,4 +730,4 @@
},
"nbformat": 4,
"nbformat_minor": 3
-}
\ No newline at end of file
+}
diff --git a/idaes_examples/notebooks/docs/flowsheets/methanol_synthesis_usr.ipynb b/idaes_examples/notebooks/docs/flowsheets/methanol_synthesis_usr.ipynb
index 6dc05f0e..86592750 100644
--- a/idaes_examples/notebooks/docs/flowsheets/methanol_synthesis_usr.ipynb
+++ b/idaes_examples/notebooks/docs/flowsheets/methanol_synthesis_usr.ipynb
@@ -51,7 +51,7 @@
"source": [
"## 1. Introduction\n",
"\n",
- "This example demonstrates a simulation of methanol synthesis from hydrogen and carbon monoxide. Each methanol flowsheet module includes several built-in methods. This notebook demonstrates building the flowsheet, implementing model scaling, initialization and solving a square problem, costing and final constrainted optimization.\n",
+ "This example demonstrates a simulation of methanol synthesis from hydrogen and carbon monoxide. Each methanol flowsheet module includes several built-in methods. This notebook demonstrates building the flowsheet, implementing model scaling, initialization and solving a square problem, costing and final constrained optimization.\n",
"\n",
"The ```build_model()``` method creates the Pyomo concrete model and builds the flowsheet by importing thermophysical and reaction properties and unit models and defining stream connections between these units. This method also implements appropriate default scaling on state and property variables.\n",
"\n",
@@ -329,7 +329,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "As expected, the process achieves a much greater revenue as a result of increasing conversion and lowering the inlet temperature to the Flash unit to encourage methanol recovery in the liquid phase. The results show a slight increase in equipment and operating costs from these changes, as well as a small loss of methanol in the exhuast."
+ "As expected, the process achieves a much greater revenue as a result of increasing conversion and lowering the inlet temperature to the Flash unit to encourage methanol recovery in the liquid phase. The results show a slight increase in equipment and operating costs from these changes, as well as a small loss of methanol in the exhaust."
]
},
{
@@ -492,7 +492,7 @@
"metadata": {},
"source": [
"## 5.3 Flowsheet Costing and Optimization\n",
- "Now that we have a well-initialized and solved flowsheet, we can add process economics and optimize the revenue. We utilize IDAES costing tools to calculate reactor and flash vessel capital cost, and implement surrogate models to account for heat exchanger capital costs, reactor operating costs and utility costs for heating, cooling and electricity. As before, revenue is determined from total liquid methanol sales, operating costs, annualized capital costs and feed raw material costs. The flowsheet report method returns key process results, which are updated for new results with the prescence of a recycle stream:"
+ "Now that we have a well-initialized and solved flowsheet, we can add process economics and optimize the revenue. We utilize IDAES costing tools to calculate reactor and flash vessel capital cost, and implement surrogate models to account for heat exchanger capital costs, reactor operating costs and utility costs for heating, cooling and electricity. As before, revenue is determined from total liquid methanol sales, operating costs, annualized capital costs and feed raw material costs. The flowsheet report method returns key process results, which are updated for new results with the presence of a recycle stream:"
]
},
{
@@ -626,4 +626,4 @@
},
"nbformat": 4,
"nbformat_minor": 3
-}
\ No newline at end of file
+}
diff --git a/idaes_examples/notebooks/docs/flowsheets/solver_captured.py b/idaes_examples/notebooks/docs/flowsheets/solver_captured.py
index e0330b40..faaab841 100644
--- a/idaes_examples/notebooks/docs/flowsheets/solver_captured.py
+++ b/idaes_examples/notebooks/docs/flowsheets/solver_captured.py
@@ -11,7 +11,7 @@
# for full copyright and license information.
#################################################################################
"""
-Captured sover
+Captured solver
"""
import json
@@ -35,7 +35,7 @@ def __init__(self, model, report=None):
def solve(self, solver):
solver = CapturedSolver(solver)
self._result = solver.solve(self._model)
- self._text = solver.ouput_text
+ self._text = solver.output_text
return self._result
def report(self):
@@ -63,7 +63,7 @@ def solve(self, model, **kwargs):
return self._solve_captured(model)
@property
- def ouput_text(self):
+ def output_text(self):
p = self._output.index(self._outsep)
output = self._output if p == -1 else self._output[:p]
return "\n".join(output)
@@ -78,7 +78,8 @@ def _solve_captured(self, m):
filename = opath.name
result = {}
tailed = Thread(
- target=self._tail_file, args=(filename, self._outsep, self._outcb, result)
+ target=self._tail_file,
+ args=(filename, self._outsep, self._outcb, result),
)
proc = Thread(
target=self._run_captured,
@@ -140,8 +141,6 @@ def _tail_file(filename, sep, output_cb, result):
if num_sep >= 2:
n1 = all_data.index(sep)
n2 = all_data.index(sep, n1 + 1)
- json_text = "".join(all_data[n1 + 1: n2])
+ json_text = "".join(all_data[n1 + 1 : n2])
r = json.loads(json_text)
result.update(r)
-
-
diff --git a/idaes_examples/notebooks/docs/flowsheets/temperature_swing_adsorption/temperature_swing_adsorption.ipynb b/idaes_examples/notebooks/docs/flowsheets/temperature_swing_adsorption/temperature_swing_adsorption.ipynb
index afb85e8e..bdb4a02b 100644
--- a/idaes_examples/notebooks/docs/flowsheets/temperature_swing_adsorption/temperature_swing_adsorption.ipynb
+++ b/idaes_examples/notebooks/docs/flowsheets/temperature_swing_adsorption/temperature_swing_adsorption.ipynb
@@ -67,7 +67,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Import Pyomo pakages \n",
+ "### Import Pyomo packages \n",
"\n",
"We will need the following components from the pyomo libraries.\n",
"\n",
@@ -132,7 +132,7 @@
"metadata": {},
"outputs": [],
"source": [
- "# import IDAES core libraries \n",
+ "# import IDAES core libraries\n",
"from idaes.core import FlowsheetBlock\n",
"from idaes.core.util.model_statistics import degrees_of_freedom\n",
"import idaes.core.util as iutil\n",
@@ -144,7 +144,10 @@
" FixedBedTSA0DInitializer,\n",
" Adsorbent,\n",
")\n",
- "from idaes.models_extra.temperature_swing_adsorption.util import tsa_summary, plot_tsa_profiles"
+ "from idaes.models_extra.temperature_swing_adsorption.util import (\n",
+ " tsa_summary,\n",
+ " plot_tsa_profiles,\n",
+ ")"
]
},
{
@@ -162,7 +165,7 @@
"metadata": {},
"outputs": [],
"source": [
- "# create concret model\n",
+ "# create concrete model\n",
"m = ConcreteModel()\n",
"\n",
"# create flowsheet\n",
@@ -224,9 +227,7 @@
"outputs": [],
"source": [
"# add tsa unit\n",
- "m.fs.tsa = FixedBedTSA0D(\n",
- " adsorbent=Adsorbent.zeolite_13x,\n",
- " number_of_beds=1)"
+ "m.fs.tsa = FixedBedTSA0D(adsorbent=Adsorbent.zeolite_13x, number_of_beds=1)"
]
},
{
@@ -248,11 +249,13 @@
"flue_gas = {\n",
" \"flow_mol_comp\": {\n",
" \"H2O\": 0.0,\n",
- " \"CO2\": 0.00960*0.12,\n",
- " \"N2\": 0.00960*0.88,\n",
- " \"O2\": 0.0},\n",
+ " \"CO2\": 0.00960 * 0.12,\n",
+ " \"N2\": 0.00960 * 0.88,\n",
+ " \"O2\": 0.0,\n",
+ " },\n",
" \"temperature\": 300.0,\n",
- " \"pressure\": 1.0e5}\n",
+ " \"pressure\": 1.0e5,\n",
+ "}\n",
"for i in m.fs.tsa.component_list:\n",
" m.fs.tsa.inlet.flow_mol_comp[:, i].fix(flue_gas[\"flow_mol_comp\"][i])\n",
"m.fs.tsa.inlet.temperature.fix(flue_gas[\"temperature\"])\n",
@@ -287,7 +290,7 @@
"m.fs.tsa.temperature_adsorption.fix(310)\n",
"m.fs.tsa.temperature_heating.fix(440)\n",
"m.fs.tsa.temperature_cooling.fix(300)\n",
- "m.fs.tsa.bed_diameter.fix(3/100)\n",
+ "m.fs.tsa.bed_diameter.fix(3 / 100)\n",
"m.fs.tsa.bed_height.fix(1.2)\n",
"\n",
"\n",
@@ -334,7 +337,7 @@
"solver_options = {\n",
" \"nlp_scaling_method\": \"user-scaling\",\n",
" \"tol\": 1e-6,\n",
- " }"
+ "}"
]
},
{
@@ -377,8 +380,8 @@
"source": [
"# initialize tsa unit\n",
"initializer = FixedBedTSA0DInitializer(\n",
- " output_level=idaeslog.INFO,\n",
- " solver_options=solver_options)\n",
+ " output_level=idaeslog.INFO, solver_options=solver_options\n",
+ ")\n",
"initializer.initialize(m.fs.tsa)"
]
},
@@ -560,7 +563,7 @@
],
"source": [
"# summary tsa\n",
- "tsa_summary(m.fs.tsa)\n"
+ "tsa_summary(m.fs.tsa)"
]
},
{
@@ -607,7 +610,7 @@
],
"source": [
"# profiles\n",
- "plot_tsa_profiles(m.fs.tsa)\n"
+ "plot_tsa_profiles(m.fs.tsa)"
]
},
{
diff --git a/idaes_examples/notebooks/docs/flowsheets/temperature_swing_adsorption/temperature_swing_adsorption_doc.ipynb b/idaes_examples/notebooks/docs/flowsheets/temperature_swing_adsorption/temperature_swing_adsorption_doc.ipynb
index 01e1d2a4..31202935 100644
--- a/idaes_examples/notebooks/docs/flowsheets/temperature_swing_adsorption/temperature_swing_adsorption_doc.ipynb
+++ b/idaes_examples/notebooks/docs/flowsheets/temperature_swing_adsorption/temperature_swing_adsorption_doc.ipynb
@@ -1,610 +1,613 @@
{
- "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": [
- "\n",
- "# TSA Adsorption Cycle for Carbon Capture\n",
- "\n",
- "\n",
- "Maintainer: Daison Yancy Caballero and Alexander Noring \n",
- "Author: Daison Yancy Caballero and Alexander Noring \n",
- "Updated: 2023-11-13 \n",
- "\n",
- "## Learning outcomes\n",
- "\n",
- "\n",
- "- Demonstrate the use of the IDAES fixed bed temperature swing adsorption (TSA) 0D unit model\n",
- "- Initialize the IDAES fixed bed TSA 0D unit model\n",
- "- Simulate the IDAES fixed bed TSA 0D unit model by solving a square problem\n",
- "- Generate and analyze results\n",
- "\n",
- "\n",
- "## Problem Statement\n",
- "\n",
- "This Jupyter notebook shows the simulation of a fixed bed TSA cycle for carbon capture by using the fixed bed TSA 0D unit model in IDAES. The fixed bed TSA model consists of a 0D equilibrium-based shortcut model composed of four steps a) heating, b) cooling, c) pressurization, and d) adsorption. Note that the equations in the IDAES fixed bed TSA 0D unit model and the input specifications used in this tutorial for the feed stream have been taken from Joss et al. 2015.\n",
- "\n",
- "\n",
- "#### A diagram of the TSA adsorption cycle is given below: \n",
- "\n",
- "![](tsa_cycle.svg)\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Step 1: Import Libraries"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Import Pyomo pakages \n",
- "\n",
- "We will need the following components from the pyomo libraries.\n",
- "\n",
- "- ConcreteModel (to create the Pyomo model that will contain the IDAES flowsheet)\n",
- "- TransformationFactory (to apply certain transformations)\n",
- "- SolverFactory (to set up the solver that will solve the problem)\n",
- "- Constraint (to write constraints)\n",
- "- Var (to declare variables)\n",
- "- Objective (to declare an objective function)\n",
- "- minimize (to minimize an objective function)\n",
- "- value (to return the numerical value of an Pyomo objects such as variables, constraints or expressions)\n",
- "- units (to handle units in Pyomo and IDAES)\n",
- "- check_optimal_termination (this method returns the solution status from solver)\n",
- "\n",
- "For further details on these components, please refer to the Pyomo documentation:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "# python libraries\n",
- "import os\n",
- "\n",
- "# pyomo libraries\n",
- "from pyomo.environ import (\n",
- " ConcreteModel,\n",
- " TransformationFactory,\n",
- " SolverFactory,\n",
- " Constraint,\n",
- " Var,\n",
- " Objective,\n",
- " minimize,\n",
- " value,\n",
- " units,\n",
- " check_optimal_termination,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Import IDAES core components\n",
- "\n",
- "To build, initialize, and solve IDAES flowsheets we will need the following core components/utilities:\n",
- "\n",
- "- FlowsheetBlock (the flowsheet block contains idaes properties, time, and unit models)\n",
- "- degrees_of_freedom (useful for debugging, this method returns the DOF of the model)\n",
- "- FixedBedTSA0D (fixed bed TSA model unit model)\n",
- "- util (some utility functions in IDAES)\n",
- "- idaeslog (it's used to set output messages like warnings or errors)\n",
- "\n",
- "For further details on these components, please refer to the IDAES documentation:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [],
- "source": [
- "# import IDAES core libraries \n",
- "from idaes.core import FlowsheetBlock\n",
- "from idaes.core.util.model_statistics import degrees_of_freedom\n",
- "import idaes.core.util as iutil\n",
- "import idaes.logger as idaeslog\n",
- "\n",
- "# import tsa unit model\n",
- "from idaes.models_extra.temperature_swing_adsorption import (\n",
- " FixedBedTSA0D,\n",
- " FixedBedTSA0DInitializer,\n",
- " Adsorbent,\n",
- ")\n",
- "from idaes.models_extra.temperature_swing_adsorption.util import tsa_summary, plot_tsa_profiles"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Step 2: Constructing the Flowsheet\n",
- "\n",
- "First, let's create a ConcreteModel and attach the flowsheet block to it."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {},
- "outputs": [],
- "source": [
- "# create concret model\n",
- "m = ConcreteModel()\n",
- "\n",
- "# create flowsheet\n",
- "m.fs = FlowsheetBlock(dynamic=False)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### 2.1: Adding the TSA Unit Model\n",
- "\n",
- "Now, we will be adding the fixed bed temperature swing adsorption (TSA) cycle model (assigned a name tsa).\n",
- "\n",
- "The TSA unit model builds variables, constraints and expressions for a solid sorbent based TSA capture system. This IDAES model can take up to 11 config arguments:\n",
- "\n",
- "1. `dynamic`: to set up the model as steady state. The IDAES fixed bed TSA 0D\n",
- " unit model only supports steady state as the dynamic nature of the adsorption\n",
- " cycle is handled in internal blocks for each cycle step of the unit. This\n",
- " config argument is used to enable the TSA unit model to connect with other\n",
- " IDAES unit models.\n",
- "2. `adsorbent`: to set up the adsorbent to be used in the fixed bed TSA system. \n",
- " Supported values currently are `Adsorbent.zeolite_13x`, `Adsorbent.mmen_mg_mof_74`, and `Adsorbent.polystyrene_amine`.\n",
- "3. `number_of_beds`: to set up the number of beds to be used in the unit model.\n",
- " This config argument accepts either an `int` (model assumes a fixed number of beds) or `None` (model calculates the number of beds).\n",
- "4. `compressor`: indicates whether a compressor unit should be added to the\n",
- " fixed bed TSA system to calculate the energy required to overcome\n",
- " the pressure drop in the system. Supported values are `True` and `False`.\n",
- "5. `compressor_properties`: indicates a property package to use in the compressor unit model.\n",
- "6. `steam_calculation`: indicates whether a method to estimate the steam flow rate\n",
- " required in the desorption step should be included. Supported values are: `SteamCalculationType.none`,\n",
- " steam calculation method is not included. `SteamCalculationType.simplified`, a surrogate model is used\n",
- " to estimate the mass flow rate of steam. `SteamCalculationType.rigorous`, a heater unit model is\n",
- " included in the TSA system assuming total saturation.\n",
- "7. `steam_properties`: indicates a property package to use for rigorous steam calculations. Currently, only the iapws95 property package is supported.\n",
- "8. `transformation_method`: to set up the discretization method to be use for the time\n",
- " domain. The discretization method must be a method recognized by the\n",
- " Pyomo `TransformationFactory`. Supported values are `dae.finite_difference` and\n",
- " `dae.collocation`.\n",
- "9. `transformation_scheme`: to set up the scheme to use when discretizing the time domain.\n",
- " Supported values are: `TransformationScheme.backward` and `TransformationScheme.forward` for finite difference transformation\n",
- " method. `TransformationScheme.lagrangeRadau` for collocation transformation method.\n",
- "10. `finite_elements`: to set up the number of finite elements to use when discretizing\n",
- " the time domain.\n",
- "11. `collocation_points`: to set up the number of collocation points to use per finite element\n",
- " when the discretization method is `dae.collocation`.\n",
- " \n",
- "\n",
- "Note: a default value defined in the IDAES unit class is used for\n",
- " a config argument when no value is passed in the time the unit model\n",
- " is called.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [],
- "source": [
- "# add tsa unit\n",
- "m.fs.tsa = FixedBedTSA0D(\n",
- " adsorbent=Adsorbent.zeolite_13x,\n",
- " number_of_beds=1)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### 2.2: Fix Specifications of Feed Stream in TSA Unit\n",
- "\n",
- "The inlet specifications of the TSA unit are fixed to match the exhaust gas stream (stream 8) of case B31B in the NETL baseline report, which is a exhaust gas stream after 90% carbon capture by means of a solvent-based capture system."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {},
- "outputs": [],
- "source": [
- "# fix inlet conditions of tsa unit - baseline case from Joss et al. 2015\n",
- "flue_gas = {\n",
- " \"flow_mol_comp\": {\n",
- " \"H2O\": 0.0,\n",
- " \"CO2\": 0.00960*0.12,\n",
- " \"N2\": 0.00960*0.88,\n",
- " \"O2\": 0.0},\n",
- " \"temperature\": 300.0,\n",
- " \"pressure\": 1.0e5}\n",
- "for i in m.fs.tsa.component_list:\n",
- " m.fs.tsa.inlet.flow_mol_comp[:, i].fix(flue_gas[\"flow_mol_comp\"][i])\n",
- "m.fs.tsa.inlet.temperature.fix(flue_gas[\"temperature\"])\n",
- "m.fs.tsa.inlet.pressure.fix(flue_gas[\"pressure\"])"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### 2.3: Fix DOF of TSA unit\n",
- "\n",
- "The degrees of freedom of the TSA unit model are: adsorption and desorption temperatures, temperatures of heating and cooling fluids, column diameter, and column height. These variables must be fixed to solve a square problem."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "The DOF of the TSA unit is 0\n"
- ]
- }
- ],
- "source": [
- "# fix design and operating variables of tsa unit - baseline case from Joss et al. 2015\n",
- "m.fs.tsa.temperature_desorption.fix(430)\n",
- "m.fs.tsa.temperature_adsorption.fix(310)\n",
- "m.fs.tsa.temperature_heating.fix(440)\n",
- "m.fs.tsa.temperature_cooling.fix(300)\n",
- "m.fs.tsa.bed_diameter.fix(3/100)\n",
- "m.fs.tsa.bed_height.fix(1.2)\n",
- "\n",
- "\n",
- "# check the degrees of freedom\n",
- "DOF = degrees_of_freedom(m)\n",
- "print(f\"The DOF of the TSA unit is {DOF}\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### 2.4: Scaling Unit Models\n",
- "\n",
- "Creating well scaled models is important for increasing the efficiency and reliability of solvers. Depending on unit models, variables and constraints are often badly scaled. IDAES unit models contain a method to scale variables and constraints to improve solver convergence. To apply the scaled factors defined in each unit model, we need to call the IDAES method `calculate_scaling_factors` in `idaes.core.util.scaling`."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "metadata": {},
- "outputs": [],
- "source": [
- "# scaling factors\n",
- "iutil.scaling.calculate_scaling_factors(m.fs.tsa)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### 2.5: Define Solver and Solver Options\n",
- "\n",
- "We select the solver that we will be using to initialize and solve the flowsheet."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "metadata": {},
- "outputs": [],
- "source": [
- "# define solver options\n",
- "solver_options = {\n",
- " \"nlp_scaling_method\": \"user-scaling\",\n",
- " \"tol\": 1e-6,\n",
- " }"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### 2.6: Initialization of Unit Models\n",
- "\n",
- "IDAES includes pre-written initialization routines for all unit models. To initialize the TSA unit model, call the method `m.fs.tsa.initialize()`.\n",
- "\n",
- "\n",
- "Note: initialize methods in IDAES unit models solve a square problem,\n",
- " so the user needs to be sure that the degrees of freedom of the unit being\n",
- " initialized are zero.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2023-10-26 15:28:28 [INFO] idaes.init.fs.tsa: Starting fixed bed TSA initialization\n",
- "2023-10-26 15:28:45 [INFO] idaes.init.fs.tsa.heating: Starting initialization of heating step.\n",
- "2023-10-26 15:28:47 [INFO] idaes.init.fs.tsa.heating: Initialization of heating step completed optimal - Optimal Solution Found.\n",
- "2023-10-26 15:29:01 [INFO] idaes.init.fs.tsa.cooling: Starting initialization of cooling step.\n",
- "2023-10-26 15:29:03 [INFO] idaes.init.fs.tsa.cooling: Initialization of cooling step completed optimal - Optimal Solution Found.\n",
- "2023-10-26 15:29:03 [INFO] idaes.init.fs.tsa.pressurization: Starting initialization of pressurization step.\n",
- "2023-10-26 15:29:03 [INFO] idaes.init.fs.tsa.pressurization: Initialization of pressurization step completed optimal - Optimal Solution Found.\n",
- "2023-10-26 15:29:03 [INFO] idaes.init.fs.tsa.adsorption: Starting initialization of adsorption step.\n",
- "2023-10-26 15:29:04 [INFO] idaes.init.fs.tsa.adsorption: Initialization of adsorption step completed optimal - Optimal Solution Found.\n",
- "2023-10-26 15:29:13 [INFO] idaes.init.fs.tsa: Initialization of fixed bed TSA model completed optimal - Optimal Solution Found.\n"
- ]
- }
- ],
- "source": [
- "# initialize tsa unit\n",
- "initializer = FixedBedTSA0DInitializer(\n",
- " output_level=idaeslog.INFO,\n",
- " solver_options=solver_options)\n",
- "initializer.initialize(m.fs.tsa)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Step 3: Solve the TSA Unit Model\n",
- "\n",
- "Now, we can simulate the TSA unit model by solving a square problem. For this, we need to set up the solver by using the Pyomo component `SolverFactory`. We will be using the solver and solver options defined during the initialization."
- ]
- },
+ "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": [
+ "\n",
+ "# TSA Adsorption Cycle for Carbon Capture\n",
+ "\n",
+ "\n",
+ "Maintainer: Daison Yancy Caballero and Alexander Noring \n",
+ "Author: Daison Yancy Caballero and Alexander Noring \n",
+ "Updated: 2023-11-13 \n",
+ "\n",
+ "## Learning outcomes\n",
+ "\n",
+ "\n",
+ "- Demonstrate the use of the IDAES fixed bed temperature swing adsorption (TSA) 0D unit model\n",
+ "- Initialize the IDAES fixed bed TSA 0D unit model\n",
+ "- Simulate the IDAES fixed bed TSA 0D unit model by solving a square problem\n",
+ "- Generate and analyze results\n",
+ "\n",
+ "\n",
+ "## Problem Statement\n",
+ "\n",
+ "This Jupyter notebook shows the simulation of a fixed bed TSA cycle for carbon capture by using the fixed bed TSA 0D unit model in IDAES. The fixed bed TSA model consists of a 0D equilibrium-based shortcut model composed of four steps a) heating, b) cooling, c) pressurization, and d) adsorption. Note that the equations in the IDAES fixed bed TSA 0D unit model and the input specifications used in this tutorial for the feed stream have been taken from Joss et al. 2015.\n",
+ "\n",
+ "\n",
+ "#### A diagram of the TSA adsorption cycle is given below: \n",
+ "\n",
+ "![](tsa_cycle.svg)\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Step 1: Import Libraries"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Import Pyomo packages \n",
+ "\n",
+ "We will need the following components from the pyomo libraries.\n",
+ "\n",
+ "- ConcreteModel (to create the Pyomo model that will contain the IDAES flowsheet)\n",
+ "- TransformationFactory (to apply certain transformations)\n",
+ "- SolverFactory (to set up the solver that will solve the problem)\n",
+ "- Constraint (to write constraints)\n",
+ "- Var (to declare variables)\n",
+ "- Objective (to declare an objective function)\n",
+ "- minimize (to minimize an objective function)\n",
+ "- value (to return the numerical value of an Pyomo objects such as variables, constraints or expressions)\n",
+ "- units (to handle units in Pyomo and IDAES)\n",
+ "- check_optimal_termination (this method returns the solution status from solver)\n",
+ "\n",
+ "For further details on these components, please refer to the Pyomo documentation:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# python libraries\n",
+ "import os\n",
+ "\n",
+ "# pyomo libraries\n",
+ "from pyomo.environ import (\n",
+ " ConcreteModel,\n",
+ " TransformationFactory,\n",
+ " SolverFactory,\n",
+ " Constraint,\n",
+ " Var,\n",
+ " Objective,\n",
+ " minimize,\n",
+ " value,\n",
+ " units,\n",
+ " check_optimal_termination,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Import IDAES core components\n",
+ "\n",
+ "To build, initialize, and solve IDAES flowsheets we will need the following core components/utilities:\n",
+ "\n",
+ "- FlowsheetBlock (the flowsheet block contains idaes properties, time, and unit models)\n",
+ "- degrees_of_freedom (useful for debugging, this method returns the DOF of the model)\n",
+ "- FixedBedTSA0D (fixed bed TSA model unit model)\n",
+ "- util (some utility functions in IDAES)\n",
+ "- idaeslog (it's used to set output messages like warnings or errors)\n",
+ "\n",
+ "For further details on these components, please refer to the IDAES documentation:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# import IDAES core libraries\n",
+ "from idaes.core import FlowsheetBlock\n",
+ "from idaes.core.util.model_statistics import degrees_of_freedom\n",
+ "import idaes.core.util as iutil\n",
+ "import idaes.logger as idaeslog\n",
+ "\n",
+ "# import tsa unit model\n",
+ "from idaes.models_extra.temperature_swing_adsorption import (\n",
+ " FixedBedTSA0D,\n",
+ " FixedBedTSA0DInitializer,\n",
+ " Adsorbent,\n",
+ ")\n",
+ "from idaes.models_extra.temperature_swing_adsorption.util import (\n",
+ " tsa_summary,\n",
+ " plot_tsa_profiles,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 2: Constructing the Flowsheet\n",
+ "\n",
+ "First, let's create a ConcreteModel and attach the flowsheet block to it."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# create concrete model\n",
+ "m = ConcreteModel()\n",
+ "\n",
+ "# create flowsheet\n",
+ "m.fs = FlowsheetBlock(dynamic=False)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 2.1: Adding the TSA Unit Model\n",
+ "\n",
+ "Now, we will be adding the fixed bed temperature swing adsorption (TSA) cycle model (assigned a name tsa).\n",
+ "\n",
+ "The TSA unit model builds variables, constraints and expressions for a solid sorbent based TSA capture system. This IDAES model can take up to 11 config arguments:\n",
+ "\n",
+ "1. `dynamic`: to set up the model as steady state. The IDAES fixed bed TSA 0D\n",
+ " unit model only supports steady state as the dynamic nature of the adsorption\n",
+ " cycle is handled in internal blocks for each cycle step of the unit. This\n",
+ " config argument is used to enable the TSA unit model to connect with other\n",
+ " IDAES unit models.\n",
+ "2. `adsorbent`: to set up the adsorbent to be used in the fixed bed TSA system. \n",
+ " Supported values currently are `Adsorbent.zeolite_13x`, `Adsorbent.mmen_mg_mof_74`, and `Adsorbent.polystyrene_amine`.\n",
+ "3. `number_of_beds`: to set up the number of beds to be used in the unit model.\n",
+ " This config argument accepts either an `int` (model assumes a fixed number of beds) or `None` (model calculates the number of beds).\n",
+ "4. `compressor`: indicates whether a compressor unit should be added to the\n",
+ " fixed bed TSA system to calculate the energy required to overcome\n",
+ " the pressure drop in the system. Supported values are `True` and `False`.\n",
+ "5. `compressor_properties`: indicates a property package to use in the compressor unit model.\n",
+ "6. `steam_calculation`: indicates whether a method to estimate the steam flow rate\n",
+ " required in the desorption step should be included. Supported values are: `SteamCalculationType.none`,\n",
+ " steam calculation method is not included. `SteamCalculationType.simplified`, a surrogate model is used\n",
+ " to estimate the mass flow rate of steam. `SteamCalculationType.rigorous`, a heater unit model is\n",
+ " included in the TSA system assuming total saturation.\n",
+ "7. `steam_properties`: indicates a property package to use for rigorous steam calculations. Currently, only the iapws95 property package is supported.\n",
+ "8. `transformation_method`: to set up the discretization method to be use for the time\n",
+ " domain. The discretization method must be a method recognized by the\n",
+ " Pyomo `TransformationFactory`. Supported values are `dae.finite_difference` and\n",
+ " `dae.collocation`.\n",
+ "9. `transformation_scheme`: to set up the scheme to use when discretizing the time domain.\n",
+ " Supported values are: `TransformationScheme.backward` and `TransformationScheme.forward` for finite difference transformation\n",
+ " method. `TransformationScheme.lagrangeRadau` for collocation transformation method.\n",
+ "10. `finite_elements`: to set up the number of finite elements to use when discretizing\n",
+ " the time domain.\n",
+ "11. `collocation_points`: to set up the number of collocation points to use per finite element\n",
+ " when the discretization method is `dae.collocation`.\n",
+ " \n",
+ "\n",
+ "Note: a default value defined in the IDAES unit class is used for\n",
+ " a config argument when no value is passed in the time the unit model\n",
+ " is called.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# add tsa unit\n",
+ "m.fs.tsa = FixedBedTSA0D(adsorbent=Adsorbent.zeolite_13x, number_of_beds=1)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 2.2: Fix Specifications of Feed Stream in TSA Unit\n",
+ "\n",
+ "The inlet specifications of the TSA unit are fixed to match the exhaust gas stream (stream 8) of case B31B in the NETL baseline report, which is a exhaust gas stream after 90% carbon capture by means of a solvent-based capture system."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# fix inlet conditions of tsa unit - baseline case from Joss et al. 2015\n",
+ "flue_gas = {\n",
+ " \"flow_mol_comp\": {\n",
+ " \"H2O\": 0.0,\n",
+ " \"CO2\": 0.00960 * 0.12,\n",
+ " \"N2\": 0.00960 * 0.88,\n",
+ " \"O2\": 0.0,\n",
+ " },\n",
+ " \"temperature\": 300.0,\n",
+ " \"pressure\": 1.0e5,\n",
+ "}\n",
+ "for i in m.fs.tsa.component_list:\n",
+ " m.fs.tsa.inlet.flow_mol_comp[:, i].fix(flue_gas[\"flow_mol_comp\"][i])\n",
+ "m.fs.tsa.inlet.temperature.fix(flue_gas[\"temperature\"])\n",
+ "m.fs.tsa.inlet.pressure.fix(flue_gas[\"pressure\"])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 2.3: Fix DOF of TSA unit\n",
+ "\n",
+ "The degrees of freedom of the TSA unit model are: adsorption and desorption temperatures, temperatures of heating and cooling fluids, column diameter, and column height. These variables must be fixed to solve a square problem."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
{
- "cell_type": "code",
- "execution_count": 11,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "WARNING: model contains export suffix 'fs.tsa.cooling.scaling_factor' that\n",
- "contains 4 component keys that are not exported as part of the NL file.\n",
- "Skipping.\n",
- "WARNING: model contains export suffix 'fs.tsa.heating.scaling_factor' that\n",
- "contains 2 component keys that are not exported as part of the NL file.\n",
- "Skipping.\n",
- "WARNING: model contains export suffix 'fs.tsa.scaling_factor' that contains 12\n",
- "component keys that are not exported as part of the NL file. Skipping.\n",
- "Ipopt 3.13.2: nlp_scaling_method=user-scaling\n",
- "tol=1e-06\n",
- "\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...: 19132\n",
- "Number of nonzeros in inequality constraint Jacobian.: 0\n",
- "Number of nonzeros in Lagrangian Hessian.............: 70375\n",
- "\n",
- "Total number of variables............................: 2815\n",
- " variables with only lower bounds: 5\n",
- " variables with lower and upper bounds: 605\n",
- " variables with only upper bounds: 1\n",
- "Total number of equality constraints.................: 2815\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.63e+01 1.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
- " 1 0.0000000e+00 1.06e+00 3.00e+03 -1.0 4.96e+00 - 9.90e-01 9.90e-01h 1\n",
- " 2 0.0000000e+00 4.90e-03 4.83e+03 -1.0 4.91e+00 - 9.90e-01 9.96e-01h 1\n",
- " 3 0.0000000e+00 2.44e-07 4.53e+00 -1.0 2.25e-02 - 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....: 1.0710209608078003e-07 2.4400780240796394e-07\n",
- "Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
- "Overall NLP error.......: 1.0710209608078003e-07 2.4400780240796394e-07\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) = 3.208\n",
- "Total CPU secs in NLP function evaluations = 0.089\n",
- "\n",
- "EXIT: Optimal Solution Found.\n"
- ]
- }
- ],
- "source": [
- "# set up solver to solve flowsheet\n",
- "solver = SolverFactory(\"ipopt\")\n",
- "solver.options = solver_options\n",
- "\n",
- "# solve flowsheet\n",
- "results = solver.solve(m, tee=True)"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "The DOF of the TSA unit is 0\n"
+ ]
+ }
+ ],
+ "source": [
+ "# fix design and operating variables of tsa unit - baseline case from Joss et al. 2015\n",
+ "m.fs.tsa.temperature_desorption.fix(430)\n",
+ "m.fs.tsa.temperature_adsorption.fix(310)\n",
+ "m.fs.tsa.temperature_heating.fix(440)\n",
+ "m.fs.tsa.temperature_cooling.fix(300)\n",
+ "m.fs.tsa.bed_diameter.fix(3 / 100)\n",
+ "m.fs.tsa.bed_height.fix(1.2)\n",
+ "\n",
+ "\n",
+ "# check the degrees of freedom\n",
+ "DOF = degrees_of_freedom(m)\n",
+ "print(f\"The DOF of the TSA unit is {DOF}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 2.4: Scaling Unit Models\n",
+ "\n",
+ "Creating well scaled models is important for increasing the efficiency and reliability of solvers. Depending on unit models, variables and constraints are often badly scaled. IDAES unit models contain a method to scale variables and constraints to improve solver convergence. To apply the scaled factors defined in each unit model, we need to call the IDAES method `calculate_scaling_factors` in `idaes.core.util.scaling`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# scaling factors\n",
+ "iutil.scaling.calculate_scaling_factors(m.fs.tsa)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 2.5: Define Solver and Solver Options\n",
+ "\n",
+ "We select the solver that we will be using to initialize and solve the flowsheet."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# define solver options\n",
+ "solver_options = {\n",
+ " \"nlp_scaling_method\": \"user-scaling\",\n",
+ " \"tol\": 1e-6,\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 2.6: Initialization of Unit Models\n",
+ "\n",
+ "IDAES includes pre-written initialization routines for all unit models. To initialize the TSA unit model, call the method `m.fs.tsa.initialize()`.\n",
+ "\n",
+ "\n",
+ "Note: initialize methods in IDAES unit models solve a square problem,\n",
+ " so the user needs to be sure that the degrees of freedom of the unit being\n",
+ " initialized are zero.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [
{
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Step 4: Viewing the Simulation Results\n",
- "\n",
- "We will call some utility methods defined in the TSA unit model to get displayed some key variables."
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2023-10-26 15:28:28 [INFO] idaes.init.fs.tsa: Starting fixed bed TSA initialization\n",
+ "2023-10-26 15:28:45 [INFO] idaes.init.fs.tsa.heating: Starting initialization of heating step.\n",
+ "2023-10-26 15:28:47 [INFO] idaes.init.fs.tsa.heating: Initialization of heating step completed optimal - Optimal Solution Found.\n",
+ "2023-10-26 15:29:01 [INFO] idaes.init.fs.tsa.cooling: Starting initialization of cooling step.\n",
+ "2023-10-26 15:29:03 [INFO] idaes.init.fs.tsa.cooling: Initialization of cooling step completed optimal - Optimal Solution Found.\n",
+ "2023-10-26 15:29:03 [INFO] idaes.init.fs.tsa.pressurization: Starting initialization of pressurization step.\n",
+ "2023-10-26 15:29:03 [INFO] idaes.init.fs.tsa.pressurization: Initialization of pressurization step completed optimal - Optimal Solution Found.\n",
+ "2023-10-26 15:29:03 [INFO] idaes.init.fs.tsa.adsorption: Starting initialization of adsorption step.\n",
+ "2023-10-26 15:29:04 [INFO] idaes.init.fs.tsa.adsorption: Initialization of adsorption step completed optimal - Optimal Solution Found.\n",
+ "2023-10-26 15:29:13 [INFO] idaes.init.fs.tsa: Initialization of fixed bed TSA model completed optimal - Optimal Solution Found.\n"
+ ]
+ }
+ ],
+ "source": [
+ "# initialize tsa unit\n",
+ "initializer = FixedBedTSA0DInitializer(\n",
+ " output_level=idaeslog.INFO, solver_options=solver_options\n",
+ ")\n",
+ "initializer.initialize(m.fs.tsa)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 3: Solve the TSA Unit Model\n",
+ "\n",
+ "Now, we can simulate the TSA unit model by solving a square problem. For this, we need to set up the solver by using the Pyomo component `SolverFactory`. We will be using the solver and solver options defined during the initialization."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [
{
- "cell_type": "code",
- "execution_count": 13,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "\n",
- "====================================================================================\n",
- "Summary - tsa\n",
- "------------------------------------------------------------------------------------ Value \n",
- " Adsorption temperature [K] 310.00\n",
- " Desorption temperature [K] 430.00\n",
- " Heating temperature [K] 440.00\n",
- " Cooling temperature [K] 300.00\n",
- " Column diameter [m] 0.030000\n",
- " Column length [m] 1.2000\n",
- " Column volume [m3] 0.00084823\n",
- " CO2 mole fraction at feed [%] 12.000\n",
- " Feed flow rate [mol/s] 0.0096000\n",
- " Feed velocity [m/s] 0.50008\n",
- " Minimum fluidization velocity [m/s] 1.5207\n",
- " Time of heating step [h] 0.37030\n",
- " Time of cooling step [h] 0.20826\n",
- " Time of pressurization step [h] 0.0051098\n",
- " Time of adsorption step [h] 0.25221\n",
- " Cycle time [h] 0.83588\n",
- " Purity [-] 0.90219\n",
- " Recovery [-] 0.89873\n",
- " Productivity [kg CO2/ton/h] 84.085\n",
- " Specific energy [MJ/kg CO2] 3.6532\n",
- " Heat duty per bed [MW] 5.1244e-05\n",
- " Heat duty total [MW] 0.00016646\n",
- " Pressure drop [Pa] 5263.6\n",
- " Number of beds 3.2484\n",
- " CO2 captured in one cycle per bed [kg/cycle] 0.042210\n",
- " Cycles per year 10480.\n",
- " Total CO2 captured per year [tonne/year] 1.4369\n",
- " Amount of flue gas processed per year [Gmol/year] 0.00030275\n",
- " Amount of flue gas processed per year (target) [Gmol/year] 0.00030275\n",
- " Amount of CO2 to atmosphere [mol/s] 0.00011667\n",
- " Concentration of CO2 emitted to atmosphere [ppm] 13803.\n",
- "====================================================================================\n"
- ]
- }
- ],
- "source": [
- "# summary tsa\n",
- "tsa_summary(m.fs.tsa)\n"
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "WARNING: model contains export suffix 'fs.tsa.cooling.scaling_factor' that\n",
+ "contains 4 component keys that are not exported as part of the NL file.\n",
+ "Skipping.\n",
+ "WARNING: model contains export suffix 'fs.tsa.heating.scaling_factor' that\n",
+ "contains 2 component keys that are not exported as part of the NL file.\n",
+ "Skipping.\n",
+ "WARNING: model contains export suffix 'fs.tsa.scaling_factor' that contains 12\n",
+ "component keys that are not exported as part of the NL file. Skipping.\n",
+ "Ipopt 3.13.2: nlp_scaling_method=user-scaling\n",
+ "tol=1e-06\n",
+ "\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...: 19132\n",
+ "Number of nonzeros in inequality constraint Jacobian.: 0\n",
+ "Number of nonzeros in Lagrangian Hessian.............: 70375\n",
+ "\n",
+ "Total number of variables............................: 2815\n",
+ " variables with only lower bounds: 5\n",
+ " variables with lower and upper bounds: 605\n",
+ " variables with only upper bounds: 1\n",
+ "Total number of equality constraints.................: 2815\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.63e+01 1.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0\n",
+ " 1 0.0000000e+00 1.06e+00 3.00e+03 -1.0 4.96e+00 - 9.90e-01 9.90e-01h 1\n",
+ " 2 0.0000000e+00 4.90e-03 4.83e+03 -1.0 4.91e+00 - 9.90e-01 9.96e-01h 1\n",
+ " 3 0.0000000e+00 2.44e-07 4.53e+00 -1.0 2.25e-02 - 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....: 1.0710209608078003e-07 2.4400780240796394e-07\n",
+ "Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00\n",
+ "Overall NLP error.......: 1.0710209608078003e-07 2.4400780240796394e-07\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) = 3.208\n",
+ "Total CPU secs in NLP function evaluations = 0.089\n",
+ "\n",
+ "EXIT: Optimal Solution Found.\n"
+ ]
+ }
+ ],
+ "source": [
+ "# set up solver to solve flowsheet\n",
+ "solver = SolverFactory(\"ipopt\")\n",
+ "solver.options = solver_options\n",
+ "\n",
+ "# solve flowsheet\n",
+ "results = solver.solve(m, tee=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 4: Viewing the Simulation Results\n",
+ "\n",
+ "We will call some utility methods defined in the TSA unit model to get displayed some key variables."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {},
+ "outputs": [
{
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Step 5: Plotting Profiles\n",
- "\n",
- "Call plots method in the FixedBedTSA0D model to generate profiles of temperature, pressure and $\\mathrm{CO_{2}}$ concentration at the outlet of the column."
- ]
- },
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "====================================================================================\n",
+ "Summary - tsa\n",
+ "------------------------------------------------------------------------------------ Value \n",
+ " Adsorption temperature [K] 310.00\n",
+ " Desorption temperature [K] 430.00\n",
+ " Heating temperature [K] 440.00\n",
+ " Cooling temperature [K] 300.00\n",
+ " Column diameter [m] 0.030000\n",
+ " Column length [m] 1.2000\n",
+ " Column volume [m3] 0.00084823\n",
+ " CO2 mole fraction at feed [%] 12.000\n",
+ " Feed flow rate [mol/s] 0.0096000\n",
+ " Feed velocity [m/s] 0.50008\n",
+ " Minimum fluidization velocity [m/s] 1.5207\n",
+ " Time of heating step [h] 0.37030\n",
+ " Time of cooling step [h] 0.20826\n",
+ " Time of pressurization step [h] 0.0051098\n",
+ " Time of adsorption step [h] 0.25221\n",
+ " Cycle time [h] 0.83588\n",
+ " Purity [-] 0.90219\n",
+ " Recovery [-] 0.89873\n",
+ " Productivity [kg CO2/ton/h] 84.085\n",
+ " Specific energy [MJ/kg CO2] 3.6532\n",
+ " Heat duty per bed [MW] 5.1244e-05\n",
+ " Heat duty total [MW] 0.00016646\n",
+ " Pressure drop [Pa] 5263.6\n",
+ " Number of beds 3.2484\n",
+ " CO2 captured in one cycle per bed [kg/cycle] 0.042210\n",
+ " Cycles per year 10480.\n",
+ " Total CO2 captured per year [tonne/year] 1.4369\n",
+ " Amount of flue gas processed per year [Gmol/year] 0.00030275\n",
+ " Amount of flue gas processed per year (target) [Gmol/year] 0.00030275\n",
+ " Amount of CO2 to atmosphere [mol/s] 0.00011667\n",
+ " Concentration of CO2 emitted to atmosphere [ppm] 13803.\n",
+ "====================================================================================\n"
+ ]
+ }
+ ],
+ "source": [
+ "# summary tsa\n",
+ "tsa_summary(m.fs.tsa)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 5: Plotting Profiles\n",
+ "\n",
+ "Call plots method in the FixedBedTSA0D model to generate profiles of temperature, pressure and $\\mathrm{CO_{2}}$ concentration at the outlet of the column."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {},
+ "outputs": [
{
- "cell_type": "code",
- "execution_count": 15,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "image/png": "iVBORw0KGgoAAAANSUhEUgAAAc8AAAKnCAYAAADgN8aBAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAAC+C0lEQVR4nOzdeVwU9f8H8Nfe3LdcgoK3qCBiIpp5cXhkZn77WpqapaVhP5WytDxSM4/KtMIj8+hbmaZlap6I4H0ieIsXisolICznssf8/kC2Ja5ddmBm2ffz8eDhMjvzmTfvWXwzM5/5fAQMwzAghBBCiN6EXAdACCGEmBoqnoQQQoiBqHgSQgghBqLiSQghhBiIiichhBBiICqehBBCiIGoeBJCCCEGouJJCCGEGEjMdQB8oNFokJaWBltbWwgEAq7DIYQQwgGGYVBQUABPT08IhbWfW1LxBJCWlgZvb2+uwyCEEMIDDx8+hJeXV63rUPEEYGtrCwBISUmBk5MTx9Hwm1KpxKFDhxAeHg6JRMJ1OLzHWr7u3AHatGEvMJ7i3edLN+88PAa8yxePaXPVqhUkHTtWu45cLoe3t7e2JtSGiiegvVRra2sLOzs7jqPhN6VSCSsrK9jZ2dEvqx5Yy1dmJtCtG3uB8RTvPl+6eefhMeBdvnisIlf2WVkQBwfXuq4+t++owxAhpmDECK4jME+6eadj0CSIXn2VlXaoeBJCCCEGouJJCCGEGIiKJyGmYMcOriMwT7p5p2PQJKi3bmWlHSqehJiCGnoHkgamm3c6Bk0C06EDK+1Q8STEFHTqxHUE5kk373QMmgRx166stEPFkxBCCDEQFU9CCCHEQDRIAiE8pdEweJBbjNuZBQieuwD2XAdkjr74ovrXxGRpFi2CiIV2qHgSwgNqDYO7Twpx6WEerj7Ox7U0OW6ky1FUpgYAtHcLwUGOYzRLUVHVvyYmSzN9OhVPQkwRwzB4nFeCy4/ycelhHpKeFcyKQqlLJhZCodJg58fhyHo7D652FhxEbMasrYGioqqvickSOziwchypeBLSwJQa4MKDp0h8JEfC/ae49CgP2YVlVdazkorQubk9/Jvbo1NzO3TytEcrF2sM+/4kRBoNLjx4iiFdPDj4CcyYWl39a2K6WDqOvCmeS5cuxezZszFt2jSsXLkSubm5mD9/Pg4dOoTU1FQ0a9YML7/8MhYtWgR7+3/u/lQ3gO9vv/2G1157rTHDJ0TraVEZEh48xfkHubiQkoukhyKoz56vtI5YKEAHD1sEeDmUf3k7oI2rDUTCqp/n7i0dAQAJVDwJ4Q1eFM/z589j3bp18Pf31y5LS0tDWloavvrqK/j5+eHBgweYPHky0tLSsONfI31s2rQJgwYN0n7v4ODQWKETgrziMpy+m4OTd7Nx9l4ubmcV/msNAVxspHjOxwlBLR3RraUj/DzsYCHR785LUEtH7OgyEBcePGU/eFK7N9+s/jUxWZpx45rGPc/CwkKMGTMG69evx+eff65d3rlzZ/zxxx/a71u3bo3FixfjjTfegEqlglj8T+gODg5wd3dv1LiJ+SopU+Pc/VycupONk3ezcS1NDoapvE7rZtZ4zscJgd52KEy5hLEjwiCVSuu1v6CWjugTMRXix/koKVPDUsrGrz7Ry9q11b8mJksTHd00imdkZCSGDh2K0NDQSsWzOvn5+bCzs6tUOCvamDhxIlq1aoXJkydjwoQJtc7HplAooFAotN/L5XIA5fO9KZVKI36apq8iP+aUJ4ZhcD29APG3snHybg6SHuZBqa5cLVs3s0avVk7o2ar87NLZurxQKpVKxKQDKpVKrzkCq+NmI8bB/01DxLhVSLifjWDfpjthO98+X+LnnoPq/Pkqr/mCb/nis4ocibp3h/LChVrX0QenxXPr1q24ePEizuvxgczOzsaiRYvwzjvvVFq+cOFCDBgwAFZWVjh06BDee+89FBYW4v/+7/9qbGvJkiVYsGBBleVxcXGwsrIy/AcxQzExMVyH0KBK1UByngDX8wS4/lQAubJy4XOQMmhn/8+XvTQfQD5U91Nw9n7V9ozN1+AnqQCA32LOIseLqWNt08eXz9eL169j3759VV7zDV/yZQqYGzdqPI7FxcV6tyNgmH9fcGocDx8+RPfu3RETE6O919mvXz907doVK1eurLSuXC5HWFgYnJycsHv37lpnTJ83bx42bdqEhw8f1rhOdWee3t7eSE9Ph7Ozs3E/WBOnVCoRExODsLCwJjdz/f2cIsQlZyP+1hOcv/+00tmllVSEXq2c0KetC3q1dkJLJyu9ziTZypfA2gZtov5Ev3YuWD+2W73b4Tu+fb7EtrZQFRRUec0XfMsXn1Xk6qVRo2o8jnK5HC4uLtqrnLXh7MwzISEBWVlZ6Nbtn/8I1Go1jh07hu+//x4KhQIikQgFBQUYNGgQbG1tsXPnzjo/IMHBwVi0aBEUCgVkMlm168hksmrfk0gk9AHUU1PIFcMwSM4swL4rGdh/Jb1KR5+Wzlbo394VAzq4IriVE2Ti+t8pMTZfBc8FAwASH+ZDJBJDWE2v3KaEN5+vXr3+iUP3Nc/wJl8mgAkJqTFXhuSQs+I5cOBAXLlypdKyCRMmoEOHDvj4448hEokgl8sREREBmUyG3bt3w8Ki7gfEk5KS4OjoWGPhJOaNYRhcfSzH/qvp2H81AynZ/zwsLRYK0MPXCQM6uKJ/B1e0crGu931Ktlkci4fVgkPIL1HiZkYB/Dxr/6uYsOTIkepfE5OlPnSIlUHdOSuetra26Ny5c6Vl1tbWcHZ2RufOnSGXyxEeHo7i4mL88ssvkMvl2o49zZo1g0gkwp49e5CZmYmePXvCwsICMTEx+OKLL/Dhhx9y8SMRnmIYBpce5WPflXTsu5KOR09LtO9JxUK80LYZhnRxx8CObrC35Odf75LRryMoPArHb2fjbEoOFc/GMmoUsG1b1dfEZIlGjwa2bze6Hc5729bk4sWLOHv2LACgTZs2ld5LSUmBj48PJBIJoqOjMWPGDDAMgzZt2mDFihWYNGkSFyETnnmQU4S/EtPwV9LjSmeYFhIh+rd3xeAuHhjQwRU2Mt7+Gvxj1y70nLy4vHjey8WE3r5cR2Qedu2q/jUxWYI9e1hph1f/a8THx2tf9+vXD3X1ZRo0aFClwREIyS0qw97LadiZ+BgXU/O0yy0kQoR2dMPQLh7o274ZrKS8+ujrpWer8kdUzt3PBcMwvLmkTIg5Mr3/QQj5F7WGwfHbT/D7hYeIuZ6p7SUrFAC927hgRGBzhHdyN40zzJq4uKBLcwdYSITILSrD7axCtHOz5Tqqps/FpfrXxHSxdBxN+H8TYu4ePS3G9guPsP3CQ6Tll2qXd25uhxGBXhgW4AFX2yYyC8mjR5CifLShk3dycOZeDhXPxvDoUfWviclSpaSAjZ4NbHQ6IqTRqNQa7L+SjrEbzqLP8jisir2NtPxSOFhJ8GYvH+yf1gd/v98Hbz/v23QKJwDMnQsA6Olb/hzy2Xu5XEZjPp7lvcprYrKE8+ez0w4rrRDSwHIKFYiOu4M+y+Mw5deLOH47GwwD9G7jjG9fD8SZ2QPx2Uud0NGjifZC/fJLAEBwq2fFMyWnzj4BhAXP8l7lNTFZwhUrWGmHLtsSXrv6OB+bT93H7ktpKFNpAADO1lKMes4brz3XAi2czWs4xQBve8jEQmQXluHukyK0cbXhOiRCzBIVT8I7KrUG+69mYPOp+0jQmYbL38se40N8MNTfQ+/pvJoamViEwBYOOHMvF2dTcqh4EsIRKp6EN0rK1Nie8BDrj9/Dw9zygQwkIgGGdPHA+F4+CPR2MN/HM9LStC97tnIuL573cjEmuCWHQZkBnbxXek1MlurBA+owRJqGvOIyfBt7G72XHcG8XdfwMLcETtZSTBvYFidnDcCq1wLRrYWj+RZOANi7V/sy+FmnoTP36L5ng9PJe6XXxGQJWJoZh4on4UxaXgkW7rmOXkuPYEXMLeQWlcHL0RILh3fCyY8HYEZYu6bVY9YYOqNmBbZwgFQkRFaBAvdz9J9CidSD7mhlNHJZkyCaMoWVduiyLWl0j/NKEB13B9svPNQOaNDRww6T+7bC0C4eEIvob7raWEhE6OrtgHP3c3H2Xg58Xay5DokQs6NX8dy9e7fBDYeFhcHS0tLg7UjTlZ5fXjS3nf+naPZs5YQp/drghbYu5n1Z1kA9Wznh3P1cnLqbg9d6tOA6HELMjl7F8+WXXzaoUYFAgNu3b6NVq1b1iYk0MZnyUqyOu4Pfzj1Embr8cZOQVs6YEdYOPXydOI7OROiM+wyUDzv47ZE7OHknGxoN0+Tn9+SMbt7/dQyIaVIfPszKJVe928jIyICrq6te69ra0rBhpLwjUHTcHfx0+oH2Gc0evk6YEdoOIa2dOY7OtAW2cISVVIScojLcyJCjk6c91yERYlb0Kp7jx4836BLsG2+8ATu7JjrSC6lTqVKNn07dR3TcHchLVQCA7i0dERVWXjTp8mw99OsHlP4zfq9ULETPVs44cjMLJ25nU/FsKLp5/9cxIKZJFBrKynHUq3hu2rRJr8YKCwthY2ODNWvWGBUUMU0aDYO/kh7j60O38Div/DnNDu62+HhwB/Rr14yKJsueb+OCIzezcPx2Nt7t25rrcAgxK3p3a/zmm29qfb+goAARERFGB0RM04nb2Rj63QlE/X4Jj/NK4GFvgS//44+9/9cH/du7UuFsAH3alk+tdO5+LkqVao6jIcS86H3P85NPPoGzszPGjRtX5b2ioiIMGjQIOTk5rAZH+C81pxif772OQ9czAQC2FmK8168NJvT2Mdsh9BrE+vVVFrVxtYGbnQyZcgXO389Fn7bNOAisidPNezXHgJge9Zo1jdth6Oeff8bYsWPh4OCAl156Sbu8qKgIERERePLkCY4ePcpCSMQUlJSp8V3cPaw9dg9lKg1EQgHG9myJaQPbwtFaynV4Tc/QoVUWCQQCPN+mGf64+AgnbmdT8WwIunmv5hgQ08MMGcJKO3oXz//85z/Iy8vD66+/jr1796Jfv37aM87MzEwcPXoUHh4erARF+IthGCRmC7D025NIfzYBde82zpg/rBNNztyQPD2r7eTQp60L/rj4CMdvZ2M2B2E1ebp5r+EYENMibtmy8ToMVZg4cSJyc3MxfPhw7Nq1C/PmzUNaWhqOHj0KT09Po4Mh/HY/uwif/HkZp+6JAJSiuYMl5r7YERGd3OmeJkd6tym/73k9XY7sQgVcbGQcR0SIeTD40u9HH32E3NxcDBw4ED4+PoiPj4eXl1dDxEZ4QqnW4Idj9/Bt7G0oVBpIBAym9GuD9wa0pfuaHGtmK0MHd1vczCjAyTvZGN61OdchEWIW9O5t+8orr2i/bt26BYlEAhcXF0ybNq3Se/W1dOlSCAQCTJ8+XbustLQUkZGRcHZ2ho2NDUaOHInMzMxK26WmpmLo0KGwsrKCq6srZs6cCZVKVe84SGWJqU8x7LsT+PJgMhQqDXq1dsKsrmq8P6A1Fc7GNHNmjW9V9Lo9cTu7saIxH7p5r+UYENOhiYpipR29zzzt7Ss/hP3666+zEgAAnD9/HuvWrYO/v3+l5TNmzMDevXuxfft22NvbY+rUqXjllVdw8uRJAIBarcbQoUPh7u6OU6dOIT09HePGjYNEIsEXX3zBWnzmqKBUia8OJuN/Zx6AYQAnaynmDO2IFzu7Yv/+/VyHZ34WLarxrefbNsP64yk4fjsbDMPQJXQ26ea9lmNATIdmwQKw8We/3sVT34ESDFVYWIgxY8Zg/fr1+Pzzz7XL8/PzsWHDBmzZsgUDBgzQxtCxY0ecOXMGPXv2xKFDh3D9+nUcPnwYbm5u6Nq1KxYtWoSPP/4Yn332GaRS6vVZH3HJWZj9xxVkyMtvqr/SrTnmDPWDk7UUSqWS4+jMlJcX8OhRtW8F+zrBQiJEhrwUNzMK0NGDRvdijW7eazkGxHSIfX1ZOY6cT0kWGRmJoUOHIjQ0tFLxTEhIgFKpRGhoqHZZhw4d0KJFC5w+fRo9e/bE6dOn0aVLF7i5uWnXiYiIwJQpU3Dt2jUEBgZWu0+FQgGFQqH9Xi6XAwCUSqVZF4dChQpLDyRj24XHAABvR0ssGu6H3s/GodXNjznnyRBs5UucnQ1VDW2IAIS0ckJccjYOX0tHGxfTnc2Ib58v3bzXdgy4wrd88Zk2R9nZNebLkDzqVTxfeeUVbN68We/xaseMGYNvvvmmzoHkt27diosXL+L8+fNV3svIyIBUKoWDg0Ol5W5ubsjIyNCuo1s4K96veK8mS5YswYIFC6osj4uLg5WVVa0xN1V35MCWOyLkKMov+fX10OBF7wLkJ5/FvuSq68fExDRyhKbN2Hy9qNFg3759Nb7vUiYAIMLOs7fRouimUfviA758vnTzXtcx4BJf8mUKNLUcx+Ji/SeX16t47tq1C0+ePNGrQYZhsGfPHixatKjW4vnw4UNMmzYNMTExsLCw0C9alsyePRtROjeN5XI5vL290b9/fzg7m9dsHwqlGisO38Gm6+X3Nps7WGDZK50RXMNUYUqlEjExMQgLC4NEImnkaE0PW/kSDB+OIbU83B2QV4LtXx/H/UIBevULg4OVaR4bvn2+dPNe1zHgAt/yxWcVuWJeeqnG41hxFVIfehVPhmHQrl07vRvVR0JCArKystCtWzftMrVajWPHjuH777/HwYMHUVZWhry8vEpnn5mZmXB3dwcAuLu749y5c5XareiNW7FOdWQyGWSyqs/DSSQSs/oAXnmUjxm/J+FOViEAYFR3b8x5sSNsLerOgbnlylhG52v79lq7xvs0k6Cdmw1uZRbi9P08vBRg2s9d8+bzpZv3Oo4Bl3iTLxPA/PZbjbkyJId6Fc+4uDi9G6zQvHntz5sNHDgQV65cqbRswoQJ6NChAz7++GN4e3tDIpEgNjYWI0eOBAAkJycjNTUVISEhAICQkBAsXrwYWVlZ2rPcmJgY2NnZwc/Pz+CYzQXDMNhwIgXLDtyEUs3AxUaGZSO7YGBHt7o3JtwYMAA4cqTWVfp3cMWtzELE3cwy+eLJG7p51+MYEP4ThYcD9ahp/6ZX8ezbt6/RO/o3W1tbdO7cudIya2trODs7a5e//fbbiIqKgpOTE+zs7PD+++8jJCQEPXv2BACEh4fDz88PY8eOxfLly5GRkYE5c+YgMjKy2jNLAuQWleHD7Zdw5GYWAGBQJ3d88UoXONF4tPx26lSdq/Rv74p1R+/h6K0nUGsYiIT0yIrRdPOuxzEg/Cc4fZqVdjjvbVubb775BkKhECNHjoRCoUBERARWr16tfV8kEuHvv//GlClTEBISAmtra4wfPx4LFy7kMGr+OnMvB9O2JiJTroBULMTcF/3wRnALei6wiQhq6QhbCzFyi8pw6VEeurVw5DokQposXhXP+Pj4St9bWFggOjoa0dHRNW7TsmVL3vaA4wu1hsF3R27j29jb0DBA62bW+H50N3oe0JR07FjnKhKREC+0a4a9l9Nx+HomFU826OZdj2NATECHDqw0w9f734QlWQWlGPPjGaw8XF44Xw3ywp73n6fCaWoSE/VaLdyv/L51xfyqxEi6edfzGBB+U1XzaGR9UPFswhIelI9Le+ZeLqylIqwc1RVfvhoAKymvLjgQfUyerNdq/Tu4QiIS4E5WIe4+KWzgoMyAbt71PAaE34SRkey0U5+NVCoVDh8+jHXr1qGgoAAAkJaWhsJC+mXlA4Zh8OvZB3jth9PIlCvQxtUGu99/Hi8H0owbJmvzZr1Ws7OQIKR1+UDxB6/VPFAI0ZNu3vU8BoTfhP/7HyvtGHwK8uDBAwwaNAipqalQKBQICwuDra0tli1bBoVCgbVr17ISGKmfUqUa83Zdxe8XysduHNzZHV++GgAbGZ1tmouITm44dusJDl3LxHv92nAdDiFNksFnntOmTUP37t3x9OlTWFr+M4bmiBEjEBsby2pwxDBpeSX477rT+P3CIwgFwKzBHbB6TDcqnE2BSP95IMI6ukEgAJIe5iEjv7QBgzIDunk34BgQHmPpOBr8v+rx48dx6tSpKjOW+Pj44PHjx6wERQyXmPoUk/6XgOxCBRysJPju9UD0aduM67AIW4qK9F7V1c4Cgd4OuJiah5jrGRgb4tNwcTV1unk34BgQ/lLl5YGNsZgMPvPUaDRQq9VVlj969Ai2trYshEQMtedSGl774QyyCxXo4G6LPVOfp8LZ1KxYYdDqEZ3Kh6ekXrdG0s27gceA8JNw5Up22jF0g/DwcKzU2blAIEBhYSHmz5/Pu0GTmzqGYbDq8G28/1siFCoNBnZwxY4pveDtZJ4zwzRpn3xi0OoVxfP03RzkF9N0VfWmm3cDjwHhJ+Hcuey0Y+gGX331FU6ePAk/Pz+UlpZi9OjR2ku2y5YtYyUoUrdSpRrTtyXhm8O3AAATn/fFD+O60/1NAgDwcbFGezdbqDQMjiTT2SchbDP4f1pvb29cunQJ27Ztw6VLl1BYWIi3334bY8aMqdSBiDSc/GIlJv7vPM7ffwqxUICFwztjdHALrsMiPBPeyQ3JmQU4eDUTIwK9uA6HkCbFoOKpVCrRoUMH/P333xgzZgzGjBnTUHGRGqTllWD8xnO4nVUIWwsx1r4RhN5tXLgOizS0a9cM3iSikzu+O3IHR289QXGZigbHqA/dvNfjGBD+USUlNX6HIYlEgtJS6vrOlVuZBXhl9SncziqEu50Ftk8OocJpLm7cMHiTTp52aOFkhRKlWjuLDjGQbt7rcQwI/whu3mSlHYPveUZGRmLZsmVQqVSsBED0k/DgKf6z5hQy5KVo3cwaf7zXCx3caXxas/Gf/xi8iUAgwLAADwDA7qQ0tiMyD7p5r8cxIPwjeu01Vtox+DrO+fPnERsbi0OHDqFLly6wtrau9P6ff/7JSmDkH+dScjFh0zkUlanRrYUDNox/Do40/ybRw7AAT0TH3UV88hPIS5Wws2DjghUhxODi6eDggJEjRzZELKQap+5m4+3NF1CiVKN3G2esH9ed7l0RvXVwt0M7NxvcyizEwasZeLW7N9chEdIkGPy/8KZNmxoiDlKNY7eeYNL/LkCh0uCFds3ww9ggWEhoiDCztHNnvTcd5u+Jr2NuYc/ldCqehtLNuxHHgPCHevt2ViaypinJeOrIzUxM/OmCdvADKpxmrnXrem86LMATAHDyTjZyChVsRWQedPNuxDEg/MG0asVKOwYXYF9fXwgEghrfv3fvnlEBkfL/5N79OQFKNYOITm747vVukIrp7xyz5u8P1LOnu4+LNfy97HH5UT72Xc3A2J4tWQ6uCdPNuxHHgPCHOCiIleNocPGcPn16pe+VSiUSExNx4MABzJw50+iAzN3Vx/l4538XoFQzGNzZHd++HgiJiAonMc4wf09cfpSPPUlpVDwJYYHBxXPatGnVLo+OjsaFCxeMDsic3c8uwpvPetX2au2Mla91pcJJWPFigAcW77uBc/dzkZ5fAg97Gg2MEGOw9j/z4MGD8ccff7DVnNnJKijF2I1nkV1Yhk6edlg3NggyMd3jJM8YOW60h70levg4AQD+SqRnPvWmm3cau7tJ0HzxBSvtsFY8d+zYAScnJ7aaMytlKg2m/HIRD3NL0NLZCpsn9IAtPY9HdNVwxccQr3RrDgD44+IjMAxjdHtmQTfvLBwDwj3N+++z0o7BxTMwMBDdunXTfgUGBsLDwwOffPIJPjFwyp41a9bA398fdnZ2sLOzQ0hICPbv3w8AuH//PgQCQbVf27dv17ZR3ftbt2419Mfi1KK/ryPhwVPYWoixeUIPNLOVcR0S4RsLC6ObGOLvAQuJEHeyCnH5UT4LQZkB3byzcAwI98QszTtt8D3P4cOHV+ptKxQK0axZM/Tr1w8dOnQwqC0vLy8sXboUbdu2BcMw+OmnnzB8+HAkJiaiQ4cOSE9Pr7T+Dz/8gC+//BKDBw+utHzTpk0YNGiQ9nsHBwdDfyzObL/wED+feQAAWDmqK3xdrOvYgpD6sbOQIKKTO3YlpWFHwiMEeDtwHRIhJsvg4vnZZ5+xtvNhw4ZV+n7x4sVYs2YNzpw5g06dOsHd3b3S+zt37sR///tf2NjYVFru4OBQZV1TcC0tH5/+dRUAMD20LQZ2dOM4ItLU/SfIC7uS0rD7Uho+HdqRnh0mpJ4MLp4ikQjp6elwdXWttDwnJweurq5Qq9X1CkStVmP79u0oKipCSEhIlfcTEhKQlJSE6OjoKu9FRkZi4sSJaNWqFSZPnowJEybU+iyqQqGAQvHPw+JyuRxA+WM3SqWyXvEbSqFUY9pviShTadC/vQum9PFptH0boyJGU4iVD9jKl3DCBGhYyPlzLezhbidDhlyBQ1fTMLgzv/7o5NvnSzfvbB0DNvEtX3xWkSPV+PFADfkyJI8CxsCeA0KhEBkZGVWKZ1paGlq3bo2SkhJDmsOVK1cQEhKC0tJS2NjYYMuWLRgyZEiV9d577z3Ex8fj+vXrlZYvWrQIAwYMgJWVFQ4dOoT58+dj+fLl+L//+78a9/nZZ59hwYIFVZZv2bIFVlZWBsVfXzvvCxGfLoSthMGsADVsqH8QaSR/pwoR81gIPwcN3u2o4TocQnijuLgYo0ePRn5+Puzsap+1Su/i+e233wIAZsyYgUWLFlW6dKpWq3Hs2DHcv38fiYmJBgVbVlaG1NRU5OfnY8eOHfjxxx9x9OhR+Pn5adcpKSmBh4cH5s6diw8++KDW9ubNm4dNmzbh4cOHNa5T3Zmnt7c30tPT4ezsbFD89XE2JRdvbCx/JvaHNwLRv32zBt8nW5RKJWJiYhAWFgaJhCp+XdjKlzgwECoDf7dqkpJdhPBVJyEUAHFRfeDpwJ9nPvn2+dLNO5vHgC18yxefVeRq2CefQJ2UVO06crkcLi4uehVPvS/bfvPNNwAAhmGwdu1aiET/3CuRSqXw8fHB2rVr9W2u0rZt2rQBAAQFBeH8+fNYtWoV1q1bp11nx44dKC4uxrhx4+psLzg4GIsWLYJCoYBMVn2vVZlMVu17EomkwT+ApUo15u4un1T39R4tEN7Zs0H311AaI1dNidH5unOHtXy383BASCtnnL6Xgz8S0xEV3p6VdtnEm8+Xbt5ZPAZs402+TIDg7t0ac2VIDvUunikpKQCA/v37488//4Sjo6PeOzGERqOpdFYIABs2bMBLL72EZs3qPkNLSkqCo6NjjYWTa6vj7yIluwiutjJ8MsSw3smEsGVMzxY4fS8HW88/xPsD29JIVoQYyOAOQ3FxcaztfPbs2Rg8eDBatGiBgoICbNmyBfHx8Th48KB2nTt37uDYsWPYt29fle337NmDzMxM9OzZExYWFoiJicEXX3yBDz/8kLUY2XT3SSHWxt8FAMwf1okGQiD6e+EFVpsL93OHi40UWQUKxN7IwiCedRziDd28s3wMCDeYPn1Qc3dS/dVrWrNHjx5h9+7dSE1NRVlZWaX3VqxYoXc7WVlZGDduHNLT02Fvbw9/f38cPHgQYWFh2nU2btwILy8vhIeHV9leIpEgOjoaM2bMAMMwaNOmDVasWIFJkybV58dqcAv2XEeZWoN+7ZthSBf6z4oY4NAhVpuTioV4tbs31sTfxa9nH1DxrIlu3lk+BoQb6n37WBlaz+DiGRsbi5deegmtWrXCzZs30blzZ9y/fx8Mw6Bbt24GtbVhw4Y61/niiy/wRQ1jEQ4aNKjS4Ah8FpechWO3nkAiEuCzYZ1qfZSGkCpGjgRYHjv69edaYO3Ruzh+OxsPcorQ0pkG6KhCN+8NcAxI4xP997+sTGxucAGePXs2PvzwQ1y5cgUWFhb4448/8PDhQ/Tt2xevvvqq0QE1RWoNgy/2lncSerOXD3xoFCFiqL17WW+yhbMVXmhb3o/gt3M19043a7p5b4BjQBqf4NkQsMYyuHjeuHFD2+tVLBajpKQENjY2WLhwIZbRrAPV2nMpDbezCmFvKcHUAW25DocQrdHBLQCUDxOpUNVvgBNCzJHBxdPa2lp7n9PDwwN3797Vvpednc1eZE2ESq3BqtjbAIB3XmgFe0vqJETqoYGGnxzYwRVudjLkFJXhwNWMBtmHSdPNuwkOAUqqwdJxNLh49uzZEydOnAAADBkyBB988AEWL16Mt956Cz179mQlqKbkz8THSMkugpO1FG/28uE6HGKq7t9vkGbFIiFG92gJANh48j5NVfZvunlvoGNAGpfq9m1W2jG4eK5YsQLBwcEAgAULFmDgwIHYtm0bfHx89OoAZE7KVBp8++ysc3LfVrCW1atzMyHA7NkN1vSYni0gFQtx6WEeLqY+bbD9mCTdvDfgMSCNR/jpp+y0Y8jKarUajx49QosW5fdJrK2tsXbtWly+fBl//PEHWrZsyUpQTcX2hId49LQEzWxlGNvTh+twiCl7NsJXQ3CxkWFE1/KJsn88ntJg+zFJunlvwGNAGo/w2VCzRrdjyMoikQjh4eF4+pT+Oq2LUq3B6rjy+8Hv9WsNSylN/UT46+0+vgCAg9cykJpTzHE0hPCfwZdtO3fujHv37jVELE3KvivpeJxXAmdrKV7v0YLrcAipVTs3W/Rp6wINA2w6RWefhNTF4OL5+eef48MPP8Tff/+N9PR0yOXySl+kfPD8dUfL/8AY38uHJhwmxsvKavBdTOzTCgDw+/mHkJfS/JAAKue9EY4BaXiqx49Zacfg4jlkyBBcunQJL730Ery8vODo6AhHR0c4ODg02GDxpubknRxcT5fDUiLC2J50H5iwoBFGtnmhrQvautqgqEyNbTRoQjndvNPoQk2CgIXRhQCOB4ZvqtYdK7/X+d/uXnC0lnIcDWkSpkwBJkxo0F0IBAJM7OOLj/+4gg0nUjCuV0vIxGZ+1UQ3741wDEjDE02dCkycaHQ7BhfPvn37Gr3Tpux2ZgGO386GUAC8/XwrrsMhxCDDuzbH14duIUNeij8vPqb79YTUoF6Dyx8/fhxvvPEGevXqhcfPrh///PPP2sETzNn/Tj8AAIR2dEMLZyuOoyHEMBYSEd55ofyPvjXxd6FSaziOiBB+Mrh4/vHHH4iIiIClpSUuXryonbg6Pz+/xtlPzIW8VIk/Lj4CUN5RiBDWNOIfpqODW8DRSoLU3GL8fTm90fbLS7p5p5ODJkEdH89KO/Xqbbt27VqsX78eEsk/47T27t0bFy9eZCUoU/VnwiMUl6nRupk1erV25joc0pQ8+yO1MVhJxXj7+fLnPqPj7kCjMeMh+3Tz3ojHgDQglo6jwcUzOTkZL1Qzo7q9vT3y8vLYiMkkMQyD/50pv2Q7vpcPzddJ2DVwYKPubmyID2xlYtzOKsSBa2Y8YLxu3hv5GJCGIYqIYKUdg4unu7s77ty5U2X5iRMn0KqV+XaQOZeSi3tPimAlFWFEYHOuwyHEKPaWEkzo7QMAWBFzC2pzPvskpBoGF89JkyZh2rRpOHv2LAQCAdLS0vDrr7/iww8/xJQpUxoiRpOw7Xz5c3EvBXjC1oKmHSOmb+KzKfTuZBXir0R2HiwnpKkw+FGVWbNmQaPRYODAgSguLsYLL7wAmUyGDz/8EO+//35DxMh7+SVK7L1S3rHiv895cxwNaZI2bmz0XdpZSDC5b2ssO3ATK2NvYViAJ6TienXQN126eefgGBD2qX/4wfDCVw2DfxMEAgE+/fRT5Obm4urVqzhz5gyePHmCRYsWsRCOadp9KQ0KlQbt3GwQ6O3AdTikKQoL42S343u1RDNbGR7mlmDbBTMcdUg37xwdA8IuJjSUlXbq/WekVCqFra0tPDw8YGNjw0owpmrb+VQAwKjnWlBHIdIwvLm5omElFeP9AW0AAN/F3kZJmZqTODijm3eOjgFhl5ilvjkGF0+VSoW5c+fC3t4ePj4+8PHxgb29PebMmQOl0rDBpNesWQN/f3/Y2dnBzs4OISEh2L9/v/b9fv36QSAQVPqaPHlypTZSU1MxdOhQWFlZwdXVFTNnzoRKpTL0x6q3q4/zcfWxHBKRgDoKkSbptedawMvRElkFCvx85j7X4RDCCwZf+n3//ffx559/Yvny5QgJCQEAnD59Gp999hlycnKwZs0avdvy8vLC0qVL0bZtWzAMg59++gnDhw9HYmIiOnXqBKC8g9LChQu121hZ/TNqj1qtxtChQ+Hu7o5Tp04hPT0d48aNg0QiabQBG3YklA+KEO7nDicax5Y0QVKxENND2+HD7ZewOv4u/tvdGw5W9Fkn5s3g4rllyxZs3boVgwcP1i7z9/eHt7c3Xn/9dYOK57Bhwyp9v3jxYqxZswZnzpzRFk8rKyu4u7tXu/2hQ4dw/fp1HD58GG5ubujatSsWLVqEjz/+GJ999hmk0ob9BVepNdoRWEYG0VknaUCzZnG6+xGBzfHj8Xu4mVGAlYdv47OXOnEaT6PRzTvHx4CwQzNzJtiY7sDg4imTyeDj41Nlua+vr1HFSq1WY/v27SgqKtKe0QLAr7/+il9++QXu7u4YNmwY5s6dqz37PH36NLp06QI3Nzft+hEREZgyZQquXbuGwMDAavelUCi0wwoC0M5DqlQqDbr0fPJuDrILFXC0kqCnj4PBl61NUcXPaA4/KxtYy9ennwIc53z2oHYYvzkBP595gFFBnmjjyn5fB959vnTzzoNj8G+8yxePVeRIMXs2JDXky5A8Glw8p06dikWLFmHTpk2QyWTlwSgUWLx4MaZOnWpoc7hy5QpCQkJQWloKGxsb7Ny5E35+fgCA0aNHo2XLlvD09MTly5fx8ccfIzk5GX/++ScAICMjo1LhBKD9PiOj5lFRlixZggULFlRZHhcXV+mycF223BECEKKjrQIxBw/ovV1TEBMTw3UIJsXYfEVMmICDmzaxFE39dXEU4spTIaJ+PoHJHRtu0Hi+fL50886XY1AdvuTLFAi9vLCvhuNYXFysdzsChmEMGjpkxIgRiI2NhUwmQ0BAAADg0qVLKCsrw8B/DV9VUeRqU1ZWhtTUVOTn52PHjh348ccfcfToUW0B1XXkyBEMHDgQd+7cQevWrfHOO+/gwYMHOHjwoHad4uJiWFtbY9++fZUuLeuq7szT29sb6enpcHbWb0xahVKNnsuOolChwq9vd0cPHye9tjN1SqUSMTExCAsLqzS2MakeW/kS29pCVVDAYmT18yCnGIO/OwmlmsH6sYHo164Zq+3z7fOlm3e+HANdfMsXn1Xk6qVRo2o8jnK5HC4uLsjPz4ednV2t7Rl85ung4ICRI0dWWuZtRBduqVSKNm3Ku8IHBQXh/PnzWLVqFdatW1dl3eDgYADQFk93d3ecO3eu0jqZmZkAUON9UqD80nPFWbMuiUSi9wcwNjkbhQoVPOwtENLaFUKheT2iYkiuCDv54kO+27jbY0JvX/xw7B6WHLiFvu3dG2TgBD59vnTj4EtM/8anfJmCmnJlSA4NLp6bGviyhUajqXRWqCspKQkA4OHhAQAICQnB4sWLkZWVBVdXVwDlly/s7OyqPXNl0+5LaQDKh+Mzt8JJOPDKK1xHoDV1QBv8kfAI954UYcOJFEzp15rrkBqObt55dAxI/TEvvww2/sfmdKyt2bNn49ixY7h//z6uXLmC2bNnIz4+HmPGjMHdu3exaNEiJCQk4P79+9i9ezfGjRuHF154Af7+/gCA8PBw+Pn5YezYsbh06RIOHjyIOXPmIDIystozS7YUlCpx+EYWAGBYgGeD7YcQrS1buI5Ay85CgtlDOgIAVsXewsNc/e8TmRzdvPPoGJD6U//8MyvtGFw8c3JyEBkZCT8/P7i4uMDJyanSlyGysrIwbtw4tG/fHgMHDsT58+dx8OBBhIWFQSqV4vDhwwgPD0eHDh3wwQcfYOTIkdizZ492e5FIhL///hsikQghISF44403MG7cuErPhTaEg9cyUabSoHUza3TyrP26OCGs6NuX6wgqGdmtOYJ9nVCq1GDerqswsOuE6dDNO8+OAakfEUtTyxl82Xbs2LG4c+cO3n77bbi5uRk1HN2GDRtqfM/b2xtHjx6ts42WLVti37599Y6hPiou2Q7v2pyG4yON4+xZriOoRCAQYPGILhi86hjikp/gwNUMDO7iwXVY7NPNO8+OAakfwb/6ydSXwcXz+PHjOHHihLanrbl5UqDAyTvZAMrvdxJirtq42mBy39b47sgdzNt9DT1bOcORRtkiZsLgy7YdOnRASUlJQ8RiEg5cTYdawyDAyx4+LtZch0PMRefOXEdQrcj+bdC6mTWeFCjw2Z5rXIfDPt288/QYEMMwndgZHcvg4rl69Wp8+umnOHr0KHJyciCXyyt9NXUHr5U/CjOkKV6iIvx14QLXEVTLQiLC1//tCqEA2JWUhgNX07kOiV26eefpMSCGUZ85w0o7BhdPBwcHyOVyDBgwAK6urnB0dISjoyMcHBzg6OjISlB8lV+sxJl7OQCAiE41P0dKCOsmTeI6ghp19XbA5L7lj6t8uvMqcgqrf9TMJOnmncfHgOhP9K+ZuerL4OI5ZswYSCQSbNmyBbGxsThy5AiOHDmCuLg4HDlyhJWg+OpIciZUGgbt3Wzpki1pXCx1r28o00LbooO7LXKKyjC3KfW+1c07z48B0Y/g119ZacfgDkNXr15FYmIi2rdvz0oApuTg1fJLtuGd3OpYkxDzIhOL8NWrAXg5+iT2XcnAX0mPMSLQi+uwCGkwBp95du/eHQ8fPmyIWHitVKnG0VtPANAlW8KBBp5ejw2dm9vj/QFtAZRfvr37pJDjiFigm3cTOAZEDywdx3pNhj1t2jTMnDkTXbp0qTIWYMXoP03N8dvZKFGq0dzBkgZGII3PRDrjTR3QBqfvZePMvVxE/noRf0X2hoWEjdkTOaKbdxM5BqR2qpwcsDEKsMFnnqNGjcKNGzfw1ltv4bnnnkPXrl0RGBio/bepOnStfIqzMD/jBoYgpF6WL+c6Ar2IhAKsei0QztZS3MwowKK/r3MdknF0824ix4DUTvjVV+y0Y+gGKSkpVb7u3bun/bcpUqk1OHyD7ncSDs2bx3UEenOzs8A3o7pCIAB+PZuKvy+ncR1S/enm3YSOAamZsJq5nOvD4Mu2LVu2ZGXHpuT8/ad4WqyEg5XEbObtJMQYL7Rrhvf6tUZ03F3M+uMKOrjboY2rDddhEcKaes2q8vPPP6N3797w9PTEgwcPAAArV67Erl27WA2OLw5dL79kO7CDG8QiTieiIcRkzAhthx6+TihUqPDO/y4gv1jJdUiEsMbgSrBmzRpERUVhyJAhyMvLg1qtBlA+eMLKlSvZjo9zDMMg9tn0Y3TJlnAmOZnrCAwmFgmxekw3NHewxL3sIry/NREqtYbrsAyjm3cTPAakKtXVq6y0Y3Dx/O6777B+/Xp8+umnEIn+6UXXvXt3XLlyhZWg+ORedhFSc4shFQnxfBsXrsMh5ioxkesI6sXFRoYfxgXBUiLCsVtPsHT/Ta5DMoxu3k30GJDKBElJrLRTrw5D1fWqlclkKCoqYiUoPom7WX7WGdzKCdYyg28RE8KO117jOoJ66+RpjxX/LZ+F6ccTKfjtXCrHERlAN+8mfAzIP0RvvMFKOwYXT19fXyRVU7kPHDiAjh07shETr8Qnlw+M0K+9K8eREGK6BnfxwPTQigEUriDmeibHERFiHL2L58KFC1FcXIyoqChERkZi27ZtYBgG586dw+LFizF79mx89NFHDRlroytSqHA2pXwg+P7tm3EcDSGmbdrAtvhvdy9oGGDqlotIeJDLdUiE1Jve1yEXLFiAyZMnY+LEibC0tMScOXNQXFyM0aNHw9PTE6tWrcJrTeyyxsk72VCqGbR0toIvDQRPuLRnD9cRGE0gEOCLEV2QXViGIzez8NbmC/hjSgjauNpyHVrNdPPeBI4BAdQ7dxr+jGY19D7z1J0lYcyYMbh9+zYKCwuRkZGBR48e4e2332YhHH6Je3bJtn97VxpViHCreXOuI2CFWCRE9OhuCGzhgPwSJcZuOIfUnGKuw6qZbt6byDEwd4ynJyvtGHTP898FxMrKCq6uTfNeIMMwiE8u7yzUjy7ZEq5168Z1BKyxlIqwYfxzaONqg/T8Ury+/gwe5vK0gOrmvQkdA3MmDg5mpR2Dime7du3g5ORU61dTkZxZgPT8UlhIhOjZypnrcAhpUpyspdgyMRitXKzxOK8Er/1wBo+e8rSAElINgy79LliwAPb29qztfM2aNVizZg3u378PAOjUqRPmzZuHwYMHIzc3F/Pnz8ehQ4eQmpqKZs2a4eWXX8aiRYsqxVDd5dTffvvN6PuvcTfLL9n2au1i2rNCEMJTrnYW+O2dnnjthzNIyS7Caz+cwS9vdec6LEL0YlDxfO2111i9TOvl5YWlS5eibdu2YBgGP/30E4YPH47ExEQwDIO0tDR89dVX8PPzw4MHDzB58mSkpaVhx44dldrZtGkTBg0apP3ewcHB6NiO3ap4RIUu2RIe+PprriNoEG52FvhtUk+89sNp3M8pxmvrz2GCL9dR6dDNexM9BuZGs3w52Dgd0rt4NkSHmWHDhlX6fvHixVizZg3OnDmDt99+G3/88Yf2vdatW2Px4sV44403oFKpIBb/E7qDgwPc3dmboLqkTI2EB08BgEYVIvzw7rtcR9Bg3O3Lz0DHbjiHO1mFWHVNhMAeeejRmgd/uOrmvQkfA3OimTSJleJZr962DUGtVmPr1q0oKipCSEhItevk5+fDzs6uUuEEgMjISLi4uKBHjx7YuHGj0bFeeJCLMrUGHvYW9IgK4Qebpj0jiYe9Jba/G4Ku3vYoVgkwbvMFxD3rsMcp3bw38WNgLsSOjuy0o++KGk3DDOh85coVhISEoLS0FDY2Nti5cyf8/PyqrJednY1FixbhnXfeqbR84cKFGDBgAKysrHDo0CG89957KCwsxP/93//VuE+FQgGFQqH9Xv5shnilUgmlUoljz35pQ1o5QaVSsfFjNhlKpbLSv6R2bOVLDEDVxHNuIxXgxzEBGLs2HjfygEk/XcDCl/zwahB3j4jo5p2Px4B+H/Wnm6Oa8mVIHgVMQ59S1qGsrAypqanIz8/Hjh078OOPP+Lo0aOVCqhcLkdYWBicnJywe/duSCSSGtubN28eNm3ahIcPH9a4zmeffYYF1UyIumXLFlhZWeHLyyI8KhJgbBs1ujfjND2EAABefPVV/L19O9dhNAq1BthyV4gL2eUXxvp5aDC8pQZCDh611s27OR2Dpqy241gx8E/FVc7acF48/y00NBStW7fGunXrAAAFBQWIiIiAlZUV/v77b1hYWNS6/d69e/Hiiy+itLQUMpms2nWqO/P09vZGeno6hJa2CF4aD4YBTn7UF6621bdhrpRKJWJiYhAWFlbrHzGkHFv5EkZFQbNiBYuR8VNFvgaGhuKHEw/xbdxdAMALbZ3xzav+sLNs3M+cbt75eAzo91F/FbkacvAgsGpVtevI5XK4uLjoVTx5N02IRqPRFja5XI6IiAjIZDLs3r27zsIJAElJSXB0dKyxcALlM8BU975EIsHZVDkYBmjnZoPmTnSPoyYSiYR+WQ1gdL6++46VTg6mQiaVIiqiA9p72OOD7Uk4djsHr6w7i+jR3dC5OXuPy9VJN+88Pgb0+2iAVatqzJUhOeS0eM6ePRuDBw9GixYtUFBQgC1btiA+Ph4HDx6EXC5HeHg4iouL8csvv0Aul2vvTTZr1gwikQh79uxBZmYmevbsCQsLC8TExOCLL77Ahx9+WO+YTt7JBlD+fCchvOHnB1y/znUUjW6ovwdaOlvh3Z8T8CCnGK+sOYX5w/wwukeLxhkyUzfvZnoMmhqxvz9w44bx7bAQS71lZWVh3LhxSE9Ph729Pfz9/XHw4EGEhYUhPj4eZ8+eBQC0adOm0nYpKSnw8fGBRCJBdHQ0ZsyYAYZh0KZNG6xYsQKTJk2qd0wVxZMeUSG8cu8e1xFwpnNze+z9v+fx4fZLOHwjC5/uvIpzKblY9HJn2Fk08NmWbt7N+Bg0KSkprDTDafHcsGFDje/169evzkdOBg0aVGlwBGOl5Zfgfk4xREIBgls1naEGCTF1DlZSrB/XHeuP38OyA8nYlZSG8ym5+OrVAPSiP3QJBwyeDLspO5tSPjBCgJc9bBv6L1pCDNG/P9cRcE4gEOCdF1rj93d7oqWzFdLySzH6x7P4bPc1lJSpG2anunmnY9AkMP36sdIOFU8d5+6VT87bm/6SJXyzfz/XEfBGUEsn7Pu/PhgT3AIAsPnUfQxadUw7pCardPNOx6BJULM0LysVTx3n7pefeVLxJLzz8stcR8Ar1jIxFo/ogs0TnoO7nQUe5BRj3MZzmLrlIrLkpeztSDfvdAyaBNHIkay0Q8VTR26xEpYSEQJbOHAdCiGVHTjAdQS81K+9Kw5/0Bdv9faFUAD8fTkdA78+io0nUlCmYmFUNN280zFoEgSHDrHSDhXPf3nO1wkyMV+f5iKE/JuNTIx5w/ywe+rz8PeyR4FChYV/X0f4N0dx4Gp6g4/LTcwTFc9/eb4NTXxNeKg5d+O7morOze2x873e+GJEF7jYyHA/pxiTf7mIV9eeRsKD3Po1qpt3OgZNA0vHkYrnv9DgCISX7t7lOgKTIBIKMDq4BeJn9sP/DWgDC4kQFx48xcg1pzF2w1ntVIN60807HYMmQXXzJivtUPHUYWMhQkeP2sczJIQTH33EdQQmxUYmRlR4e8R/2B+juntDLBTg+O1sjFxzCmM3nMXpuzn6Xc7VzTsdgyZBOGsWO+2w0koTEejtABEXUzcQUpdvv+U6ApPkbm+BZf/xx5EP+lUqoq+vP4MXvzuBnYmPau9YpJt3OgZNgjA6mp12WGmliQhqwc4kqYQQfmnhbIVl//FH3If9MCa4BSwkQlxLk2PGtkvos/wIouPu4EmBou6GCHmGiqeOIHpEhZAmzdvJCotHdMHpWQMxM6I9XG1lyJQr8OXBZIQsicW7P19A3M0sqDXUQ5fUjndTknGpgztNQUZ4KieH6wiaFEdrKSL7t8GkPq2w51Iafjn7AImpeTh4LRMHr2XCw94CLwc2R3jibXgXPjsjfZAGFPLr7FSlVKJACeQUKiCWsPBcaxNWkausOw9gW6aCldS48kfFU4dYRCfihKd++w2YOJHrKJocqViIkUFeGBnkheSMAmw7/xB/Jj5Cen4p1sTfRe6lg9gWEAEAGKXzml/EmHPhKNdBmAgxrmxYDM8P3se00LZGtUTVghBTMHUq1xE0ee3dbTFvmB/OfjIQ370eiBf9PbAwZq32fd3XxHSxdRzpzJMQQnTIxCIMC/DEsABP4C0h7i8dWv7GSp3XPKFUKrFv3z4MGTIEEgnNBFWbilxJxUKjzzoBOvMkhBBCDEbFkxBTcPo01xGYJ9280zFoElTHjrHSDhVPQkxBQQHXEZgn3bzTMWgSBIWFrLRDxZMQUxAeznUE5kk373QMmgTRkCGstEPFkxBCCDEQFU9CCCHEQFQ8CTEFP/3EdQTmSTfvdAyaBPWGDay0w2nxXLNmDfz9/WFnZwc7OzuEhIRg//792vdLS0sRGRkJZ2dn2NjYYOTIkcjMzKzURmpqKoYOHQorKyu4urpi5syZUKlUjf2jENKwXniB6wjMk27e6Rg0CUyfPqy0w2nx9PLywtKlS5GQkIALFy5gwIABGD58OK5duwYAmDFjBvbs2YPt27fj6NGjSEtLwyuvvKLdXq1WY+jQoSgrK8OpU6fw008/YfPmzZg3bx5XPxIhDcPXl+sIzJNu3ukYNAnidu3YaYeVVupp2LBhlb5fvHgx1qxZgzNnzsDLywsbNmzAli1bMGDAAADApk2b0LFjR5w5cwY9e/bEoUOHcP36dRw+fBhubm7o2rUrFi1ahI8//hifffYZpFIpFz8WIYSQJo43w/Op1Wps374dRUVFCAkJQUJCApRKJUJDQ7XrdOjQAS1atMDp06fRs2dPnD59Gl26dIGbm5t2nYiICEyZMgXXrl1DYGBgtftSKBRQKP6ZHSE/Px8AkJub20A/XdOhVCpRXFyMnJwcGg5MD2zlS8wwUJnBzCp8+3zp5p2Px4Bv+eKzilzJazmOBc+e5WWYuqek47x4XrlyBSEhISgtLYWNjQ127twJPz8/JCUlQSqVwsHBodL6bm5uyMjIAABkZGRUKpwV71e8V5MlS5ZgwYIFVZa3Y+l0npAG4eLCdQTmSTfvdAyahjqOY0FBAezt7Wtdh/Pi2b59eyQlJSE/Px87duzA+PHjcfRow06vM3v2bERFRWm/z8vLQ8uWLZGamlpnwsydXC6Ht7c3Hj58CDs7O67D4T3Kl2EoX4ahfOlPn1wxDIOCggJ4enrW2R7nxVMqlaJNmzYAgKCgIJw/fx6rVq3CqFGjUFZWhry8vEpnn5mZmXB3dwcAuLu749y5c5Xaq+iNW7FOdWQyGWQyWZXl9vb29AHUU0UPaaIfypdhKF+GoXzpr65c6XsCxbvnPDUaDRQKBYKCgiCRSBAbG6t9Lzk5GampqQgJCQEAhISE4MqVK8jKytKuExMTAzs7O/j5+TV67IQQQswDp2ees2fPxuDBg9GiRQsUFBRgy5YtiI+Px8GDB2Fvb4+3334bUVFRcHJygp2dHd5//32EhISgZ8+eAIDw8HD4+flh7NixWL58OTIyMjBnzhxERkZWe2ZJCCGEsIHT4pmVlYVx48YhPT0d9vb28Pf3x8GDBxEWFgYA+OabbyAUCjFy5EgoFApERERg9erV2u1FIhH+/vtvTJkyBSEhIbC2tsb48eOxcOFCg+KQyWSYP38+FVw9UK4MQ/kyDOXLMJQv/bGdKwGjT59cQgghhGjx7p4nIYQQwndUPAkhhBADUfEkhBBCDETFkxBCCDGQ2RfP6Oho+Pj4wMLCAsHBwVUGXTBXx44dw7Bhw+Dp6QmBQIC//vqr0vsMw2DevHnw8PCApaUlQkNDcfv2bW6C5diSJUvw3HPPwdbWFq6urnj55ZeRnJxcaR19ptczF2xMRWiuli5dCoFAgOnTp2uXUb4q++yzzyAQCCp9dejQQfs+W/ky6+K5bds2REVFYf78+bh48SICAgIQERFRadAFc1VUVISAgABER0dX+/7y5cvx7bffYu3atTh79iysra0RERGB0tLSRo6Ue0ePHkVkZCTOnDmDmJgYKJVKhIeHo6ioSLtOXdPrmRNjpyI0V+fPn8e6devg7+9faTnlq6pOnTohPT1d+3XixAnte6zlizFjPXr0YCIjI7Xfq9VqxtPTk1myZAmHUfEPAGbnzp3a7zUaDePu7s58+eWX2mV5eXmMTCZjfvvtNw4i5JesrCwGAHP06FGGYcpzI5FImO3bt2vXuXHjBgOAOX36NFdh8oqjoyPz448/Uq5qUFBQwLRt25aJiYlh+vbty0ybNo1hGPpsVWf+/PlMQEBAte+xmS+zPfMsKytDQkJCpSnPhEIhQkNDcfr0aQ4j47+UlBRkZGRUyp29vT2Cg4Mpd/hnijsnJycAqHN6PXOmVquxdetWvaciNFeRkZEYOnRopbwA9Nmqye3bt+Hp6YlWrVphzJgxSE1NBcBuvjgfGJ4r2dnZUKvV1U5pdvPmTY6iMg0V071Vl7vapoIzBxqNBtOnT0fv3r3RuXNnAOX5qmt6PXNjzFSE5mbr1q24ePEizp8/X+U9+mxVFRwcjM2bN6N9+/ZIT0/HggUL0KdPH1y9epXVfJlt8SSkIURGRuLq1auV7rGQqriYitAUPXz4ENOmTUNMTAwsLCy4DsckDB48WPva398fwcHBaNmyJX7//XdYWlqyth+zvWzr4uICkUhUpZeV7pRnpHoV+aHcVTZ16lT8/fffiIuLg5eXl3a5u7u7dno9Xeacr4qpCIOCgrBkyRIEBARg1apVlKt/SUhIQFZWFrp16waxWAyxWIyjR4/i22+/hVgshpubG+WrDg4ODmjXrh3u3LnD6ufLbIunVCpFUFBQpSnPNBoNYmNjtVOeker5+vrC3d29Uu7kcjnOnj1rlrljGAZTp07Fzp07ceTIEfj6+lZ6X5/p9cydIVMRmpOBAwfiypUrSEpK0n51794dY8aM0b6mfNWusLAQd+/ehYeHB7ufLyM6NZm8rVu3MjKZjNm8eTNz/fp15p133mEcHByYjIwMrkPjXEFBAZOYmMgkJiYyAJgVK1YwiYmJzIMHDxiGYZilS5cyDg4OzK5du5jLly8zw4cPZ3x9fZmSkhKOI298U6ZMYezt7Zn4+HgmPT1d+1VcXKxdZ/LkyUyLFi2YI0eOMBcuXGBCQkKYkJAQDqPmzqxZs5ijR48yKSkpzOXLl5lZs2YxAoGAOXToEMMwlKu66Pa2ZRjK17998MEHTHx8PJOSksKcPHmSCQ0NZVxcXJisrCyGYdjLl1kXT4ZhmO+++45p0aIFI5VKmR49ejBnzpzhOiReiIuLYwBU+Ro/fjzDMOWPq8ydO5dxc3NjZDIZM3DgQCY5OZnboDlSXZ4AMJs2bdKuU1JSwrz33nuMo6MjY2VlxYwYMYJJT0/nLmgOvfXWW0zLli0ZqVTKNGvWjBk4cKC2cDIM5aou/y6elK/KRo0axXh4eDBSqZRp3rw5M2rUKObOnTva99nKF01JRgghhBjIbO95EkIIIfVFxZMQQggxEBVPQgghxEBUPAkhhBADUfEkhBBCDETFkxBCCDEQFU9CCCHEQFQ8CSGEEANR8SSEEEIMRMWTEEIIMRAVT0IIIcRAVDwJIYQQA1HxJIQQQgwk5joAPtBoNEhLS4OtrS0EAgHX4RBCCOEAwzAoKCiAp6cnhMLazy2peAJIS0uDt7c312EQQgjhgYcPH8LLy6vWdah4ArC1tQUApKSkwMnJieNo+E2pVOLQoUMIDw+HRCLhOhzeYy1fd+4AbdqwFxhP8e7zpZt3Hh4D3uWLx7S5atUKko4dq11HLpfD29tbWxNqQ8UT0F6qtbW1hZ2dHcfR8JtSqYSVlRXs7Ozol1UPrOUrMxPo1o29wHiKd58v3bzz8BjwLl88VpEr+6wsiIODa11Xn9t31GGIEFMwYgTXEZgn3bzTMWgSRK++yko7VDwJIYQQA/GqeB47dgzDhg2Dp6cnBAIB/vrrrzq3iY+PR7du3SCTydCmTRts3ry5weMkhBBi3nhVPIuKihAQEIDo6Gi91k9JScHQoUPRv39/JCUlYfr06Zg4cSIOHjzYwJES0sh27OA6AvOkm3c6Bk2CeutWVtrhVYehwYMHY/DgwXqvv3btWvj6+uLrr78GAHTs2BEnTpzAN998g4iICIP3f/JuDuyy1QZvZ07UajVuPBXA5nY2RCIR1+HwHhv5EggE6ObbBnX3/yOse9Yr896TQmTJXFGanMVxQJXR76P+KnLVzN0VntlF8HGxNqo9XhVPQ50+fRqhoaGVlkVERGD69Om1bqdQKKBQKLTfy+VyAMDU3y5BKLNiPc6mR4S1Ny9yHYQJMT5ft75+BcriQpbi4S+lUlnpX66JO3VC6sMsDFxxHDe/HIH2H+7kOqRq0O+j/kSYMbEvovddxvv9W1d515DPnUkXz4yMDLi5uVVa5ubmBrlcjpKSElhaWla73ZIlS7BgwYIqyz2tGIgtmAaJlZD6UGmAjBIBGIbB/n37uA6n0cTExHAdAgDgRY0GOw/GgWHK/6v0sqb/H0yeAMi8fwv79iVXeau4uFjvZky6eNbX7NmzERUVpf2+4sHYv95/Ac7OzhxGxn9KpRIxMTEICwuj58r0YGy+sgsVCFl2FAAwaNBgCIVNe/hIvn2+hEIhQkJ6YeXVc4AAiJtl+O2ghsS3fPFZRa6kIiE+n1D97cGKq5D6MOni6e7ujszMzErLMjMzYWdnV+NZJwDIZDLIZLIqyyUSCX0A9US5Mkx98+VsW34fa/kL4/B/agHsZeaRc958vr74AmJx+TH4cdBERPIhpmrwJl8mQLNoUY25MiSHvOpta6iQkBDExsZWWhYTE4OQkBCOIiKEXVKxEFZSETb0GIG8kjKuwzE/Oleotr/AzsP1hFuaOvrE6ItXxbOwsBBJSUlISkoCUP4oSlJSElJTUwGUX24dN26cdv3Jkyfj3r17+Oijj3Dz5k2sXr0av//+O2bMmMFF+IQ0CAdLCa6vGIm8Yn50ojEr1v/0yNz/6RAOAyFsETs4sNIOr4rnhQsXEBgYiMDAQABAVFQUAgMDMW/ePABAenq6tpACgK+vL/bu3YuYmBgEBATg66+/xo8//livx1QI4Ss7SwlEGg3yS6h4Njr1P4+uiRh6jK1JULNzHHl1z7Nfv35gmJp7s1U3elC/fv2QmJjYgFERwi0Hq/L7MHlUPAnhDV6deRJCqnKwlGJHl4HIL6Z7no3uzTe1Lw8EDeIuDsIajc6tP2NQ8SSE5+wtJfg0YipdtuXC2rXalytGUl+KpkCj5/CvdaHiSQjPOVhJsHfT/1GHIS48638BAOtXvsNhIIQt4ueeY6UdKp6E8Jy9lQRtch7SPU8u3Lihfdky6wGHgRDW3LzJSjNUPAnhOXvL8g5DdNmWEP6g4kkIzzlYSpHQvCPy6bJt4+vVS/vyastOHAZC2MKwNIgOFU9CeM7BSoLRr3+Bp9TbtvEdOaJ9GfXuCg4DIWxRHzrESjtUPAnhOQcrCb7ftQxP6cyz8Y0apX0575eFHAZC2CIaPZqVdqh4EsJzTtZShN0+g7zisloHESENYNcu7cvnr5/kMBDCFsGePay0Q8WTEJ5ztJICAFQaBgUKFcfREEIAKp6E8J6FRISnVnYAgLwiunTbqFxctC/zre05DISwRueYGoOKJyEm4JXZ2wCAOg01tkePtC9f/fR3DgMhbFGlpLDSDhVPQkzA+/H/AwDkUvFsXHPnal++dXAjh4EQtgjnz2enHVZaIYQ0qJFHtgIA8qh4Nq4vv9S+fO3oNg4DIWwRrmDnkSMqnoSYAIGg/N9cuudJCC9Q8STEhNCZJyH8QMWTEBOw/rfjAIDcIiqejSotTfty5JztHAZC2KJ6wM4A/1Q8CTEBnZNOAABNS9bY9u7Vvux54wyHgRC2CPbtY6UdKp6EmIDeyz8BQGeejW7SJO3LmX98zWEghC2iKVNYaYeKJyEm4Fl/IXrOkxCeoOJJiCl4Vj2peBLCD1Q8CTEB2bsPAACeFilpcPjGFB+vfTn93W+4i4OwRn34MCvtUPEkxATYW0kAAGVqDeSlNDg8IVzjZfGMjo6Gj48PLCwsEBwcjHPnztW6/sqVK9G+fXtYWlrC29sbM2bMQGlpaSNFS0jDk4UOhI1MDADILlRwHI0Z6ddP+3LluhncxUFYIwoNZaUd3hXPbdu2ISoqCvPnz8fFixcREBCAiIgIZGVlVbv+li1bMGvWLMyfPx83btzAhg0bsG3bNnzyySeNHDkhDcvFpnxqspxCuu9JCNd4VzxXrFiBSZMmYcKECfDz88PatWthZWWFjRurH5T51KlT6N27N0aPHg0fHx+Eh4fj9ddfr/NslRBT42wjA0BnnoTwgZjrAHSVlZUhISEBs2fP1i4TCoUIDQ3F6dOnq92mV69e+OWXX3Du3Dn06NED9+7dw759+zB27Nga96NQKKBQ/PMfkFwuBwAolUoolfQQem0q8kN50g9b+RKsWQMnYfl9z8z84iabf759vgRr1kClUgMAvhz5AT7iSVwV+JYvPqvIUdn330NYQ74MySOvimd2djbUajXc3NwqLXdzc8PNmzer3Wb06NHIzs7G888/D4ZhoFKpMHny5Fov2y5ZsgQLFiyosjwuLg5WVlbG/RBmIiYmhusQTIqx+ZJIJCh+kgFAiDNJ1+GUc5WdwHiKL58viUSCW6dPARAjtqU/OrM0Og3b+JIvUxBrYQFlDcexuLhY73bqVTyjoqIM3mbOnDlwcnKqz+5qFR8fjy+++AKrV69GcHAw7ty5g2nTpmHRokWYqzMXn67Zs2dX+hnkcjm8vb3Rv39/ODs7sx5jU6JUKhETE4OwsDBIJBKuw+E9tvIltrXFjV2JOJl5D44eLTBkiB+LUfIH3z5fYltbOF97iJVXz2H/ivEQfl7EdUiV8C1ffFaRq8ETJ0JVUFDtOhVXIfVRr+K5cuVKhISEQCqV6rX+iRMnMHXq1DqLp4uLC0QiETIzMystz8zMhLu7e7XbzJ07F2PHjsXEiRMBAF26dEFRURHeeecdfPrppxAKq97WlclkkMlkVZZLJBL6AOqJcmUYNvLlamcJoHxasqaeez59vsRiEYDycSr4EtO/8SlfpqCmXBmSw3pftt25cydcXV31WtfW1lav9aRSKYKCghAbG4uXX34ZAKDRaBAbG4upU6dWu01xcXGVAikSlX/Y6WFy0pS4POswlEPj2xLCuXoVz02bNsHe3l7v9detW1flPmZNoqKiMH78eHTv3h09evTAypUrUVRUhAkTJgAAxo0bh+bNm2PJkiUAgGHDhmHFihUIDAzUXradO3cuhg0bpi2ihJi8mTPhYku9bRvdzJnal1v7jsI4DkMh7NBERYGNylCv4jl+/HgAgFqtxsmTJ+Hv7w8HB4ca1x89erTebY8aNQpPnjzBvHnzkJGRga5du+LAgQPa4puamlrpTHPOnDkQCASYM2cOHj9+jGbNmmHYsGFYvHhxfX40Qvhp0SI4PykEAGQXUPFsNIsWAQ9yAQAbI96i4tkEaBYs4K54VhCJRAgPD8eNGzdqLZ6Gmjp1ao2XaeN1xpoEALFYjPnz52P+/Pms7Z8Q3vHygsudFABAUZkaJWVqWErpykqD8/ICTl4GAGxf/F9g5hOOAyLGEvv6Ao8eGd2O0YMkdO7cGffu3TM6EEJILbKzYSsTQyou/5WlS7eNJDtb+9K+KJ/DQAhrdI6pMYwunp9//jk+/PBD/P3330hPT4dcLq/0RQhhh0AggIt1eQ93Kp6EcMvoQRKGDBkCAHjppZcgEAi0yxmGgUAggFqtNnYXhJDhwwEALrYypOWX0vi2jeVZ3gHghF9vDOAwFMIOZtgwCOperU5GF8+4uDgWwiCE1GrbNgD/PK5CZ56NZNs2bYehhW/Mo+LZBKi3bGFlUHeji2ffvn1ZCIMQUqsBA4AjR7Qzq1DxbCQDBgCbdgAAVqyLAmYmchwQMZYoPBxg4aSPtbFti4uLkZqairKyypeT/P392doFIebr1CkAujOr0GXbRvEs7wDQ+cE1DgMhbBHUMMmIoYwunk+ePMGECROwf//+at+ne56EsIcu2xLCD0Zf+p0+fTry8vJw9uxZWFpa4sCBA/jpp5/Qtm1b7N69m40YCSEdOwIAXbZtbM/yDgAPXFtyGAhhTYcOrDRj9JnnkSNHsGvXLnTv3h1CoRAtW7ZEWFgY7OzssGTJEgwdOpSNOAkxb4nl99pc6LJt40pM1HYYmjT9B8RzGw1hger8ebAxhL7RZ55FRUXaAeIdHR3x5En5CBxdunTBxYsXjW2eEAIAkycD0Bkcns48G8ezvANA1B/fcBgIYYswMpKddoxtoH379khOTgYABAQEYN26dXj8+DHWrl0LDw8PowMkhADYvBnAP5dtnxYroVRrOAzITDzLOwAMSjjAXRyENcL//Y+Vdoy+bDtt2jSkp6cDAObPn49Bgwbh119/hVQqxWadDx4hxHgOVlIIBYCGAXKLyuBmZ8F1SISYJaOL5xtvvKF9HRQUhAcPHuDmzZto0aIFXFxcjG2eEAIAz6bXEwkFcLGRIatAgSy5gopnQ9OZ1lAtoIH4mwSWpqpkY6AFLYZhYGlpiW7dulHhJIRNRUXalxUFM1NeylU05kMn74MX7+MwEMIWVV4eK+2wUjw3bNiAzp07w8LCAhYWFujcuTN+/PFHNpomhADAihXalxXFM4OKZ8PTyfurx7ZzGAhhi3DlSnbaMbaBefPmYdq0aRg2bBi2b9+O7du3Y9iwYZgxYwbmzZvHRoyEkE8+0b50ty/vcUtnno1AJ++TDtAJQVMgnDuXlXaMvue5Zs0arF+/Hq+//rp22UsvvQR/f3+8//77WLhwobG7IITocK8488yn4kkIV4w+81QqlejevXuV5UFBQVCpVMY2Twj5F7psSwj3jC6eY8eOxZo1a6os/+GHHzBmzBhjmyeEAMC1fwYld7enDkONRifvb36wkcNACFtUSUmstFOvy7ZRUVHa1wKBAD/++CMOHTqEnj17AgDOnj2L1NRUjBs3jpUgCTF7N24ArVsDoMu2jerGDaBLLwBAy8xUjoMhbBDcvMnK+Lb1Kp6JiZXntAsKCgIA3L17FwDg4uICFxcXXLtGU/gQwor//AcoLS+Wrs+Kp7xUhZIyNSyl9Pxhg/nPf4DkNADAgl8+AzZ9Uvv6hPdEr72m/V0yRr2KZxwLE4kSQurHzkIMS4kIJUo1MuWl8HGx5jokQswOq4MkEEIankAg0N73pE5DhHCjXsXzlVdegVwu13v9MWPGICsrqz67IoQAwM6dlb51s6NnPRuFTt7njKPH7poC9XZ2BruoV/HctWsXnjx5ArlcXudXfn4+9uzZg8LCQr3bj46Oho+PDywsLBAcHIxz587Vun5eXh4iIyPh4eEBmUyGdu3aYd8+GkqLNCHPOgtVoE5DjUQn72nOnhwGQtjCtGrFSjv1uufJMAzatWvHSgD/tm3bNkRFRWHt2rUIDg7GypUrERERgeTkZO28obrKysoQFhYGV1dX7NixA82bN8eDBw/g4ODQIPERwgl//0qdHNyeXbZNp+LZsPz9tR2GNn4zEVhOTxCYOnFQkGl1GGrevLle661YsQKTJk3ChAkTAABr167F3r17sXHjRsyaNavK+hs3bkRubi5OnToFiaR8fnAfHx+D4yPElDR3sAQApOeXcBwJIeapXsWzb9++bMcBoPwsMiEhAbNnz9YuEwqFCA0NxenTp6vdZvfu3QgJCUFkZCR27dqFZs2aYfTo0fj4448hqmHqGYVCAYVCof2+4v6tUqmEUqlk8SdqeiryQ3nSD1v5EgNQ6bThalP+h+Kjp8VN6ljw7fMlBqBSqQEADPgTVwW+5YvPdHNUU74MyaPRY9uyKTs7G2q1Gm5ubpWWu7m54ebNm9Vuc+/ePRw5cgRjxozBvn37cOfOHbz33ntQKpWYP39+tdssWbIECxYsqLI8Li4OVlZWxv8gZiAmJobrEEyKsflq9cYbuKdzH/9xEQCIcT9L3iTv7/Pl89XqjTdw+vQpAGJ8N/BNtOVprvmSL1Nw9V+/S7qKi4v1bkfAMAzDVlDGSktLQ/PmzXHq1CmEhIRol3/00Uc4evQozp49W2Wbdu3aobS0FCkpKdozzRUrVuDLL79Eenp6tfup7szT29sb6enpcHZ2ZvmnalqUSiViYmIQFhamvUxOatZQ+ZKXKBH0Rfntk8tzBzaZgRL4+Pm6mJqHUevPoaWTFQ7PeJ7rcCrhY774Sp9cyeVyuLi4ID8/H3Z2drW2x6szTxcXF4hEImRmZlZanpmZCXd392q38fDwgEQiqXSJtmPHjsjIyEBZWRmkUmmVbWQyGWQyWZXlEomEPoB6olwZxuh8WVhU6uTgJBbDRiZGoUKFJ8UqtLa2YCFK/uDN58vCAuJnHYZiPo2A5KMyjgOqHm/yZQIsnZwgqKHDkCE55NUgCVKpFEFBQYiNjdUu02g0iI2NrXQmqqt37964c+cONBqNdtmtW7fg4eFRbeEkpCkQCATwdCgvmGl51GmIkMbGSvFUqVQ4fPgw1q1bh4KCAgDll2ANebazQlRUFNavX4+ffvoJN27cwJQpU1BUVKTtfTtu3LhKHYqmTJmC3NxcTJs2Dbdu3cLevXvxxRdfIDIyko0fjRDe8nzW4/bxUyqehDQ2oy/bPnjwAIMGDUJqaioUCgXCwsJga2uLZcuWQaFQYO3atQa1N2rUKDx58gTz5s1DRkYGunbtigMHDmg7EaWmpkIo/Kfme3t74+DBg5gxYwb8/f3RvHlzTJs2DR9//LGxPxoh/PH221UWVTyuQmeeDUgn73ufG4IRHIZC2KGZMAFs9BAwunhOmzYN3bt3x6VLlyp1thkxYgQmTZpUrzanTp2KqVOnVvtefHx8lWUhISE4c+ZMvfZFiEmIjq6yqOLM8xEVz4YTHQ08yAUArBoxjYpnE6D59ltWiqfRl22PHz+OOXPmVLm/6OPjg8ePHxvbPCEEALp0qbLIy/FZ8aTLtg1HJ+8bV1Q9+yemRxwYyEo7RhdPjUYDtVpdZfmjR49ga2trbPOEEAC4fbvKohZO5c8kP8gpauxozIdO3r2yH3EYCGHNnTusNGN08QwPD8fKlSu13wsEAhQWFmL+/PkYMmSIsc0TQmrg41w+j2emXIFSZdU/YAkhDcfo4vnVV1/h5MmT8PPzQ2lpKUaPHq29ZLts2TI2YiSEvPBClUUOVhLYWpR3W0jN1X9kFGIAnbxf8vXnMBDCFqZPH1baMbrDkLe3Ny5duoRt27bh0qVLKCwsxNtvv40xY8bA0tKSjRgJIYcOVVkkEAjQ0tkKVx/L8SCnGO3c6DYJ6w4d0nYYmjnpS8RzGw1hgXrfPlae0TSqDaVSidatW+P27dsYM2YMli9fjtWrV2PixIlUOAlh08iR1S5u6VR+6ZbuezYQnbwv+F/1Y2UT0yL6739Zaceo4imRSFDKwrxohJA67N1b7eIWzhWdhuiybYPQyXvITXocrikQ7N/PSjtGn71GRkZi2bJlUKlUbMRDCDFAy4oet3TPk5BGZfQ9z/PnzyM2NhaHDh1Cly5dYG1tXen9P//809hdEEJqmBih5bMet6l02bZh6OQ919YJ1R8FYlJq+F0ylNHF08HBASNruB9DCGHJ/fvVLm757LLto6clUKk1EIt4NdeD6bt/X9th6LXZv1GHoSZAdfs22Jh/xujiuWnTJhbCIITUavZsYMmSKovd7SwgFQtRptIgPb8U3k40mTurZs8GJs8EAEzavx6Y2Z/jgIixhJ9+Cixfbnw7LMRCCGlo33xT7WKhUADvZ8P03adLt+zTyfurx3dwGAhhi/Dbb1lpx+gzT19fXwgEghrfv3fvnrG7IITUoqWzNe4+KcKDnGL0act1NISYB6OL5/Tp0yt9r1QqkZiYiAMHDmDmzJnGNk8IqUPFfU8aZYiQxsPKlGTViY6OxoULF4xtnhACAFlZNb7VkgaIbzhZWcDT8sfwXp73J/ZxHA4xnurxY1Y6DDXYPc/Bgwfjjz/+aKjmCTEvtfwuVTyuQgMlNACdvL9w5RiHgRC2CHbuZKWdBiueO3bsgJOTU0M1T4h5mTKlxrd8XMqLZ0p2EdQaprEiMg86eY/auZK7OAhrRFOnstKO0ZdtAwMDK3UYYhgGGRkZePLkCVavXm1s84SQOng7WkIqFkKh0uDx0xLtkH2EkIZjdPF8+eWXK30vFArRrFkz9OvXDx06dDC2eUJIHcQiIVq5WONmRgFuZxVQ8SSkERhdPOfPp5kGCGlwJ07U+nYbVxvczCjAnaxCDOzo1khBmQGdvL8/5Vus4zAUwg51fLzxhQ8s3PO8ePEirly5ov1+165dePnll/HJJ5+grKzM2OYJIQCgUNT6dlvX8rk8b2cVNkY05kMn7xK1ksNACGvq+F3Sl9HF891338WtW7cAlA+IMGrUKFhZWWH79u346KOPjA6QEAJg4MBa327rZgMAuEPFk106eV/xwwccBkLYIoqIYKUdo4vnrVu30LVrVwDA9u3b0bdvX2zZsgWbN2+mR1UIaSRtXP8pngxDPW4JaWhGF0+GYaDRaAAAhw8fxpAhQwAA3t7eyM7Orleb0dHR8PHxgYWFBYKDg3Hu3Dm9ttu6dSsEAkGVTkyENHU+ztYQCQUoVKiQKWfnshQhpGZGF8/u3bvj888/x88//4yjR49i6NChAICUlBS4uRnecWHbtm2IiorC/PnzcfHiRQQEBCAiIgJZtYywAgD379/Hhx9+iD59+tTr5yCE1zZurPVtqVgIn2e9bG9lFjRGROZBJ+/LXqXbUE2B+ocfWGnH6OK5cuVKXLx4EVOnTsWnn36KNm3aACgfJKFXr14Gt7dixQpMmjQJEyZMgJ+fH9auXQsrKytsrOU/D7VajTFjxmDBggVo1apVvX8WQngrLKzOVdq7l3caupkhb+hozIdO3i+0DeIwEMIWJjSUlXaM7rHr7+9fqbdthS+//BIikcigtsrKypCQkIDZs2drlwmFQoSGhuL06dM1brdw4UK4urri7bffxvHjx+vcj0KhgEKnx5VcXv6fjVKphFJJPepqU5EfypN+2MqX2NsbqoLazyjbudpgH4Brj/NN9vjw7fMl9vaG6tpDAMDvX4yCcg6/xg/mW774rCJH4latoKzhd8mQPBpdPB8+fAiBQAAvLy8AwLlz57Blyxb4+fnhnXfeMait7OxsqNXqKpd73dzccPPmzWq3OXHiBDZs2ICkpCS997NkyRIsWLCgyvK4uDhYWdED5vqIiYnhOgSTYmy+XtRosG9f7cOSF+UKAIhw/nYa9u17aNT+uMaXz9eLGg1Onz4FQAwwqPMYcIUv+TIFmlp+l4qL9R8f2ujiOXr0aLzzzjsYO3YsMjIyEBYWhk6dOuHXX39FRkYG5s2bZ+wualRQUICxY8di/fr1cHFx0Xu72bNnIyoqSvu9XC6Ht7c3+vfvD2dn54YItclQKpWIiYlBWFgYJBI25iZo2tjKl1Ao1HbGq0lAXgnWJx/HE4UQoeFhkIpNb657vn2+hEIhQkJ6YeXVc4AAdR6Dxsa3fPFZRa5q+12quAqpD6OL59WrV9GjRw8AwO+//47OnTvj5MmTOHToECZPnmxQ8XRxcYFIJEJmZmal5ZmZmXB3d6+y/t27d3H//n0MGzZMu6yi569YLEZycjJat25dZTuZTAaZTFZluUQioQ+gnihXhjE6X7Nm1bl9SxcxbC3EKChV4cFTBfw87eq/P47x5vM1axbE4vLbT7/1ex1v8iGmavAmXyZAM3NmjbkyJIdG/2mqVCq1hejw4cN46aWXAAAdOnRAenq6QW1JpVIEBQUhNjZWu0yj0SA2NhYhISFV1u/QoQOuXLmCpKQk7ddLL72E/v37IykpCd7e3kb8ZITwyGef1bmKQCBAR/fygnkjnToNsUIn75vD3+QsDMIeDUtXQ40unp06dcLatWtx/PhxxMTEYNCgQQCAtLS0el0CjYqKwvr16/HTTz/hxo0bmDJlCoqKijBhwgQAwLhx47QdiiwsLNC5c+dKXw4ODrC1tUXnzp0hlUqN/fEI4QcPD71W69S8vHheeZzfkNGYD528/7HoPxwGQtgibtGCnXaMbWDZsmUYMWIEvvzyS4wfPx4BAQEAgN27d2sv5xpi1KhRePLkCebNm4eMjAx07doVBw4c0HYiSk1NhVBoevdyCDHK06d6rdbV2wEAkPQwr+FiMSc6ebctoednmwQ9f5fqYnTx7NevH7KzsyGXy+Ho6Khd/s4779S75+rUqVMxtYYJS+Pj42vddvPmzfXaJyFNQUXxvJ4mR5lKY5KdhggxBaz8ZjEMg4SEBKxbtw4Fz56fkUql9NgHIWx55RW9VmvhZAUHKwnK1Bq678kGnbwf60yjlzUFDEvDtxpdPB88eIAuXbpg+PDhiIyMxJMnTwCUX8798MMPjQ6QEAJgyxa9VhMIBAjwcgAAXHqU13DxmAudvH8+eg6HgRC2qH/+mZV2jC6e06ZNQ/fu3fH06VNYWlpql48YMaJSr1lCiBH69tV71QC678kenbyvXDOduzgIa0R1TO+nL6PveR4/fhynTp2q0rPVx8cHjx8/NrZ5QggAnD2r96pdve0BAJeoeBpPJ+9+D29wGAhhi0DPWbrqYvSZp0ajgVqtrrL80aNHsLW1NbZ5QoiBKi7b3n1SBHkpjXlKSEMwuniGh4dj5cqV2u8FAgEKCwsxf/583g1lRYjJ6txZ71WdbWTwdiq/hXLlET3vaRSdvKe4+XIYCGEL06kTK+0YXTy/+uornDx5En5+figtLcXo0aO1l2yXLVvGRoyEkAsXDFq94uyT7nsaSSfv705by2EghC3qM2dYacfo4unt7Y1Lly7h008/xYwZMxAYGIilS5ciMTERrq6ubMRICJk0yaDVA1uUP3N94X5uQ0RjPnTy/uGOrzgMhLBFNHkyK+0YVTyVSiVat26N27dvY8yYMVi+fDlWr16NiRMnVup5SwgxkoHd64N9nQAAF+4/hUqtaYiIzINO3sMv0rRfTYHg119Zaceo4imRSFBaWspKIIQQ9nT0sCufYUWhwnUaLIEQ1hl92TYyMhLLli2DSqViIx5CSHUMnORAJBRozz7P3MtpiIjMg07elSKa8qtJYGnCEKOf8zx//jxiY2Nx6NAhdOnSBdbW1pXe//PPP43dBSHEgEl6KwT7OuPwjSycvZeLd16oOq8t0YNcDjwov288dNHfiOc2GsICVU4O2PgzyOgzTwcHB4wcORIRERHw9PSEvb19pS9CCAuWLzd4k56tyqcEPJeSS/c960sn76/Fb+UwEMIW4VfsdPwy+sxz06ZNbMRBCKnNvHnARx8ZtImfpx3sLSXIL1Hi0qN8BLV0rHsjUtm8ecCoiQCAtw5tArCO23iI0YQLFgDP5oQ2qp36bqjRaLBs2TL07t0bzz33HGbNmoWSkhKjAyKEsEMkFOD5Ni4AgKO3nnAcDSFNS72L5+LFi/HJJ5/AxsYGzZs3x6pVqxAZGclmbIQQI73Qrrx4HqPiSQir6l08//e//2H16tU4ePAg/vrrL+zZswe//vorNBq6t0II65KT67XZC+2aAQAuP8rD06IyNiMyDzp5HzvzJw4DIWxRXb3KSjv1Lp6pqamVxq4NDQ2FQCBAWloaK4ERQnQkJtZrMw97S3Rwt4WGAWJvZrEclBnQyXvbx3c4DISwRZCUxEo79S6eKpUKFhYWlZZJJBIolTSLAyGse+21em86qLM7AODA1XS2ojEfOnmft2URh4EQtojeeIOVdurd25ZhGLz55puQyWTaZaWlpZg8eXKlZz3pOU9CuDWosztWHr6NY7ezUahQwUZmdCd7QsxevX+Lxo8fX2XZGyxVdEIIe9q72cLXxRop2UWIu5mFYQGeXIdEiMmrd/Gk5zsJaUR79tR7U4FAgIhO7lh79C4OXMug4mkInbzPfnMxvuYwFMIO9c6dxg9wABZGGCKENILmzY3avOK+Z9zNLJQq1WxEZB508p5t78JhIIQtjCc7fzxS8STEFHTrZtTmAV728LC3QHGZGvHJ9Myn3nTyvn7VuxwGQtgiDg5mpR1eFs/o6Gj4+PjAwsICwcHBOHfuXI3rrl+/Hn369IGjoyMcHR0RGhpa6/qEmCOBQKC9XPvnxUccR0OI6eNd8dy2bRuioqIwf/58XLx4EQEBAYiIiEBWVvXPqMXHx+P1119HXFwcTp8+DW9vb4SHh+Px48eNHDkh/PafIC8AwJGbWcguVHAcDSGmjXfFc8WKFZg0aRImTJgAPz8/rF27FlZWVti4cWO16//6669477330LVrV3To0AE//vgjNBoNYmNjGzlyQhrQ18Z3VWnnZosAL3uoNAz+SqQ/LvWik/fVL07hMBDCFk09ZiiqDq8e+CorK0NCQgJm64x4LxQKERoaitOnT+vVRnFxMZRKJZycnGpcR6FQQKH45y9v+bO5EpVKJQ3yUIeK/FCe9MNavt56C2Ah5yO6euDSo3xsOZuKsT28IBQKjG6TTbz7fL31FlRphQCA3T2GIZIvcT3Du3zxWEWOFG++CUkN+TIkj7wqntnZ2VCr1XBzc6u03M3NDTdv3tSrjY8//hienp4IDQ2tcZ0lS5ZgwYIFVZbHxcXBysrKsKDNVExMDNchmBRj8/Xiq6/i7+3bjY7DUgXIRCLcyy7Cit8OwM+RMbrNhsCXz9eLr76K0xu3AxBj/9wh2Odn/DFoCHzJlymQubjU+LtUXFysdzu8Kp7GWrp0KbZu3Yr4+PgqQwfqmj17NqKiorTfy+VyeHt7o3///nB2dm6MUE2WUqlETEwMwsLCIJGwMR9708ZWvoRCYaWxpI1xU5KMTace4KrSFR8OCWKlTbbw7fMlFAoREtILK6+eAwRg7RiwhW/54rOKXNX2u1RxFVIfvCqeLi4uEIlEyMzMrLQ8MzMT7u7utW771VdfYenSpTh8+DD8/f1rXVcmk1UaVrCCRCKhD6CeKFeGYSNfbOX7redb4afTD3Dybg7u5ZSivbstK+2yiU+fL7FYBAAQgL1jwDY+5csU1JQrQ3LIqw5DUqkUQUFBlTr7VHT+CQkJqXG75cuXY9GiRThw4AC6d+/eGKES0rjeZe8ZQ28nK4T7lf8xuvFECmvtNkk6ed8dPIzDQAhbNJMmsdIOr4onAERFRWH9+vX46aefcOPGDUyZMgVFRUWYMGECAGDcuHGVOhQtW7YMc+fOxcaNG+Hj44OMjAxkZGSgsLCQqx+BEPatWsVqc2/38QUA7Ex6jCx5KattNyk6ef9++FQOAyFs0axYwUo7vCueo0aNwldffYV58+aha9euSEpKwoEDB7SdiFJTU5Ge/s/USmvWrEFZWRn+85//wMPDQ/v11VdfcfUjEMI+Pz9Wm+ve0hFBLR1RptLg+ziap7JGOnnf/NUEDgMhbBHXcVtP73ZYaYVlU6dOxdSp1f+VFx8fX+n7+/fvN3xAhHDt3j1WmxMIBPgwvD1eX38Gv51LxaQ+reDtRD3Nq9DJu2duGoeBENaksHOrgndnnoSQxhHS2hl92rpAqWawIuYW1+EQYlKoeBJiCvr3b5BmZ0a0h0AA7Ex8jHMpuQ2yD5Omk/fE1oEcBkLYwvTrx0o7VDwJMQX79zdIs/5eDnjtuRYAgLl/XYVSrWmQ/Zgsnbx//PZSDgMhbFEbMTeuLiqehJiCl19usKY/imgPRysJkjML6NGVf9PJ++c/zeUuDsIa0ciRrLRDxZMQU3DgQIM17WgtxezBHQEAXx+6hetp+o+y0uTp5L1HMk112BQIDh1ipR0qnoQQvNrdC6Ed3VCm1mDa1kSUKtVch0QIr1HxJMQUNG/eoM0LBAIsG9kFzWxluJ1ViEV/X2/Q/ZkMnbw/sXPhMBDCGpZ+l6h4EmIK7t5t8F0428jw9asBEAiAX8+m4tezDxp8n7ynk/cxs37lMBDCFpWeM3TVhYonIabgo48aZTcvtGuGD8PbAwDm77qGIzcz69iiidPJ+7t713EYCGGLcNYsdtphpRVCSMP69ttG29V7/VpjRGBzqDQMJv9yEafuZDfavnlHJ+8jT/7JYSCELcLoaHbaYaUVQkiTIRAIsPw//gjzc0OZSoOJ/7tAAygQ8i9UPAkhVUhEQnw/OhB92rqguEyNNzacxd7L6XVvSIiZoOJJiCnIyWn0XcrEIvwwtjvCn52BRm65iNXxd6DRMI0eC2d08v7SZ39xFwdhjSojg5V2qHgSYgp++42T3VpKRVjzRhDe7OUDAFh+IBlv/3QeOYUKTuJpdDp5H5h4hMNACFsE27ax0g4VT0JMQQ1T9DUGkVCAz17qhC9GdIFMLERc8hMMWnUc+66kg2Ga+FmoTt6n7Wq8Tluk4YimTWOlHSqehBC9jA5ugV1Te6ONqw2eFCjw3q8XMWHzedx7Ush1aIQ0OiqehBC9dXC3w9/vP4//G9AGEpEA8clPEPbNMcz64zIe55VwHR4hjYaKJyGm4PRpriPQspCIEBXeHgemv4D+7ZtBrWGw9fxD9F0eh//7LRFJD/OazuVcnby/F/k9h4EQtqiOHWOlHTErrRBCGlZBAdcRVNG6mQ02TeiBhAe5+PrQLZy6m4Pdl9Kw+1Ia2rvZ4pVuzTG8a3O421twHWr9FRQATuUvrRTF3MZCWCEoZOc2A515EmIKwsO5jqBGQS2dsGVST/z9/vN4pVtzSMVCJGcWYMn+mwhZGotX157C2qN3cSuzwPTOSHXy/tWPjTNEImlYoiFDWGmHzjwJIazo3NweK/7bFfOHdcK+K+nYefExzt3Pxfn7T3H+/lMs3X8TLjZSBPs6o2crJwS2cEQ7N1tIxfQ3PDE9VDwJIayyt5Tg9R4t8HqPFnicV4IjNzJx+EYWztzLQXZhGfZeScfeK+WjFUlEArRzs0UnTzt08rSHj5MFnipgXgMxEJNExZMQU/DTT1xHUC/NHSwxNsQHY0N8oFCpcelhPs7ey8HZlFxcfpQHeakK19LkuJYmB/Do2VZiLL0SCx9na7R0toKHvSU8HSzgbm8JD3sLuNtZwNVOBplY1PA/gE7el/x3FuY3/B5JA1Nv2MBK4eNl8YyOjsaXX36JjIwMBAQE4LvvvkOPHj1qXH/79u2YO3cu7t+/j7Zt22LZsmUYwtJ1bUJ44YUXuI7AaDKxCD18ndDD1wnvA2AYBo+eljwrnvm4kS7HvSeFuJ9ThFKlBjczCnAzo+aOUjYyMRytJXCyksLRWvrPv9ZS2FqIYSMTw1omhu2zf22eLbORiWElFUEgENQd9AsvAGXlLy+18mcnEYRTTJ8+rLTDu+K5bds2REVFYe3atQgODsbKlSsRERGB5ORkuLq6Vln/1KlTeP3117FkyRK8+OKL2LJlC15++WVcvHgRnTt35uAnIKQB+PoCpaVcR8EqgUAAbycreDtZYVBndwCAUqnEnr374N+zHx7mKZCaW4z0/FJk5JeU/ysvRXp+KcpUGhQqVChUqPAw1/DnSwUCwEoigkwigkwsfPYlgrTitaT8+3XvPI9pi/YDALYuHQ0s+g+rOSCNT9yuHSu/S7wrnitWrMCkSZMwYcIEAMDatWuxd+9ebNy4EbOqmcR01apVGDRoEGbOnAkAWLRoEWJiYvD9999j7dq1jRo7IcR4IgHQ0tkKbdztq32fYRjklyjxtFiJ3KIyPC0qQ25RGXKL/3ldUKpCUZmq/F9F+VfBs381DMAwQFGZGkVl6lpj0WjKz47L6XGmSswGr4pnWVkZEhISMHv2bO0yoVCI0NBQnK7hIfHTp08jKiqq0rKIiAj89ddfNe5HoVBAofhnYOv8/HwAQG4uzVlYF6VSieLiYuTk5EAikXAdDu+xlS8xw0DFwcwqjc2QfNkJADsbwMdGjPL/yqzqbJ9hGJQqNShSqFCsVEOp0kChYlCmUkOh1kCh0qDs2ZdCrUHxCmB6H08wAIq/AQp4dgzo91F/FbmS1/K7VPDseWp9HqniVfHMzs6GWq2Gm5tbpeVubm64efNmtdtkZGRUu35GLdPOLFmyBAsWLKiyvF27dvWImpBG4uLCdQRmZxIAvNgNABAF0DFoKuo4jgUFBbC3r/7KRwVeFc/GMnv27Epnq3l5eWjZsiVSU1PrTJi5k8vl8Pb2xsOHD2FnZ8d1OLxH+TIM5cswlC/96ZMrhmFQUFAAT0/POtvjVfF0cXGBSCRCZmZmpeWZmZlwd3evdht3d3eD1gcAmUwGmUxWZbm9vT19APVkZ2dHuTIA5cswlC/DUL70V1eu9D2B4tXQHlKpFEFBQYiNjdUu02g0iI2NRUhISLXbhISEVFofAGJiYmpcnxBCCDEWr848ASAqKgrjx49H9+7d0aNHD6xcuRJFRUXa3rfjxo1D8+bNsWTJEgDAtGnT0LdvX3z99dcYOnQotm7digsXLuCHH37g8scghBDShPGueI4aNQpPnjzBvHnzkJGRga5du+LAgQPaTkGpqakQCv85Ye7Vqxe2bNmCOXPm4JNPPkHbtm3x119/GfSMp0wmw/z586u9lEsqo1wZhvJlGMqXYShf+mM7VwLG5KY5IIQQQrjFq3uehBBCiCmg4kkIIYQYiIonIYQQYiAqnoQQQoiBzL54RkdHw8fHBxYWFggODsa5c+e4DokXjh07hmHDhsHT0xMCgaDKWMEMw2DevHnw8PCApaUlQkNDcfv2bW6C5diSJUvw3HPPwdbWFq6urnj55ZeRnJxcaZ3S0lJERkbC2dkZNjY2GDlyZJXBPczFmjVr4O/vr31YPSQkBPv379e+T7mq2dKlSyEQCDB9+nTtMspXZZ999hkEAkGlrw4dOmjfZytfZl08K6Y/mz9/Pi5evIiAgABEREQgKyuL69A4V1RUhICAAERHR1f7/vLly/Htt99i7dq1OHv2LKytrREREYHSJjZtlj6OHj2KyMhInDlzBjExMVAqlQgPD0dRUZF2nRkzZmDPnj3Yvn07jh49irS0NLzyyiscRs0dLy8vLF26FAkJCbhw4QIGDBiA4cOH49q1awAoVzU5f/481q1bB3//yvOKUr6q6tSpE9LT07VfJ06c0L7HWr4YM9ajRw8mMjJS+71arWY8PT2ZJUuWcBgV/wBgdu7cqf1eo9Ew7u7uzJdffqldlpeXx8hkMua3337jIEJ+ycrKYgAwR48eZRimPDcSiYTZvn27dp0bN24wAJjTp09zFSavODo6Mj/++CPlqgYFBQVM27ZtmZiYGKZv377MtGnTGIahz1Z15s+fzwQEBFT7Hpv5Mtszz4rpz0JDQ7XL6pr+jJRLSUlBRkZGpdzZ29sjODiYcod/prhzcnICACQkJECpVFbKV4cOHdCiRQuzz5darcbWrVtRVFSEkJAQylUNIiMjMXTo0Ep5AeizVZPbt2/D09MTrVq1wpgxY5CamgqA3XzxboShxlKf6c9IuYrp3gydCs4caDQaTJ8+Hb1799aOcpWRkQGpVAoHB4dK65pzvq5cuYKQkBCUlpbCxsYGO3fuhJ+fH5KSkihX/7J161ZcvHgR58+fr/IefbaqCg4OxubNm9G+fXukp6djwYIF6NOnD65evcpqvsy2eBLSECIjI3H16tVK91hIVe3bt0dSUhLy8/OxY8cOjB8/HkePHuU6LN55+PAhpk2bhpiYGFhYWHAdjkkYPHiw9rW/vz+Cg4PRsmVL/P7777C0tGRtP2Z72bY+05+RchX5odxVNnXqVPz999+Ii4uDl5eXdrm7uzvKysqQl5dXaX1zzpdUKkWbNm0QFBSEJUuWICAgAKtWraJc/UtCQgKysrLQrVs3iMViiMViHD16FN9++y3EYjHc3NwoX3VwcHBAu3btcOfOHVY/X2ZbPOsz/Rkp5+vrC3d390q5k8vlOHv2rFnmjmEYTJ06FTt37sSRI0fg6+tb6f2goCBIJJJK+UpOTkZqaqpZ5qs6Go0GCoWCcvUvAwcOxJUrV5CUlKT96t69O8aMGaN9TfmqXWFhIe7evQsPDw92P19GdGoyeVu3bmVkMhmzefNm5vr168w777zDODg4MBkZGVyHxrmCggImMTGRSUxMZAAwK1asYBITE5kHDx4wDMMwS5cuZRwcHJhdu3Yxly9fZoYPH874+voyJSUlHEfe+KZMmcLY29sz8fHxTHp6uvaruLhYu87kyZOZFi1aMEeOHGEuXLjAhISEMCEhIRxGzZ1Zs2YxR48eZVJSUpjLly8zs2bNYgQCAXPo0CGGYShXddHtbcswlK9/++CDD5j4+HgmJSWFOXnyJBMaGsq4uLgwWVlZDMOwly+zLp4MwzDfffcd06JFC0YqlTI9evRgzpw5w3VIvBAXF8cAqPI1fvx4hmHKH1eZO3cu4+bmxshkMmbgwIFMcnIyt0FzpLo8AWA2bdqkXaekpIR57733GEdHR8bKyooZMWIEk56ezl3QHHrrrbeYli1bMlKplGnWrBkzcOBAbeFkGMpVXf5dPClflY0aNYrx8PBgpFIp07x5c2bUqFHMnTt3tO+zlS+akowQQggxkNne8ySEEELqi4onIYQQYiAqnoQQQoiBqHgSQgghBqLiSQghhBiIiichhBBiICqehBBCiIGoeBJCCCEGouJJCCGEGIiKJyGEEGIgKp6EEEKIgah4EkIIIQai4kkIIYQYiIonIYQQYiAx1wHwgUajQVpaGmxtbSEQCLgOhxBCCAcYhkFBQQE8PT0hFNZ+bknFE0BaWhq8vb25DoMQQggPPHz4EF5eXrWuQ8UTgK2tLQAgJSUFTk5OHEfDb0qlEocOHUJ4eDgkEgnX4fAea/m6cwdo04a9wHiKd58v3bzz8BjwLl88ps1Vq1aQdOxY7TpyuRze3t7amlAbKp6A9lKtra0t7OzsOI6G35RKJaysrGBnZ0e/rHpgLV+ZmUC3buwFxlO8+3zp5p2Hx4B3+eKxilzZZ2VBHBxc67r63L6jDkOEmIIRI7iOwDzp5p2OQZMgevVVVtrhVfE8duwYhg0bBk9PTwgEAvz11191bhMfH49u3bpBJpOhTZs22Lx5c4PHSQghxLzxqngWFRUhICAA0dHReq2fkpKCoUOHon///khKSsL06dMxceJEHDx4sIEjJYQQYs54dc9z8ODBGDx4sN7rr127Fr6+vvj6668BAB07dsSJEyfwzTffICIioqHCJKTx7djBdQTmSTfvdAyaBPXWrawUPl4VT0OdPn0aoaGhlZZFRERg+vTptW6nUCigUCi038vlcgDlN5SVSiXrcbKBYRikPi3B/ewiZMgVyMgvRXZRGUrK1ChRqlGqVKNEqYFCqYaGATQMAw1Tvt0/ryu+L3+/npGguFiEr24eA2D8M7H1jaKx1fST1tSvQPBsC+ZZvr5OPq5dVvM2Ne/DMzsPaTfj9NqiPu3XRMDSPmra4N9LGTAokIuwNuVUjZ026sq5/uvXQOcN9yeFyLh/HEKBABPdbRHBs/8fKv6/4uv/W3yizVWbNmBqyJcheTTp4pmRkQE3N7dKy9zc3CCXy1FSUgJLS8tqt1uyZAkWLFhQZXlcXBysrKwaJNb6UGuAxBwBrj0V4K5cgHwlXwZwECBHUcp1ECZEAJSWGNXC/q/eQvsPd7IUD98J8Li4kOsgAADbvxqnzXvYtBHY98d2jiOqXkxMDNchmAxpt274e3v1x7G4uFjvdky6eNbX7NmzERUVpf2+4tme/v37w9nZmcPIymk0DH499xDrT9xHev4/RUoiEqC1izXc7S3gYW+BZjYyWMlEsJCIYCkRwlIiglQshEgogFAggEAACAUCCJ/9+8/3z/5Gr0ctVqtUOHfuHHr06AGRmJ2PD1/+JKhJjWfHNbyhu1ilUuH8+XN47rkeEIlEhrWP8qsFACD5Rogtbz+ns031Wxl6QaGm9Q1tv6bd1hZPdftQq9RIuHgR3QIDIf7X56vmfdQQq4ExVbdY/I0QM8Pb4stDtwEBMGTIkBpa5YZSqURMTAzCwsLoUZU6VORKKBTWeBwrrkLqw+D//Xbv3m3oJggLC6vxLNAY7u7uyMzMrLQsMzMTdnZ2te5PJpNBJpNVWS6RSDj/AOYWlWH6tiQcu/UEAOBiI8PrPbzRq7ULAls4wEJS/X/AjUWpVCLrBtDd14XzXJkCpVKJ7JtAj1ZG5ksA9Grryl5gPKVUKlF8j0H/ju78+HwJgJ6tXQDchgDgR0zV4MP/XaakplwZkkODi+fLL79s0PoCgQC3b99Gq1atDN1VnUJCQrBv375Ky2JiYhASEsL6vhrDo6fFGLXuDB7nlcBCIsSsQR3wWo8WnBdMwgNffMF1BOZJJ+/rB01EJIehEHZoFi0CG/+j1utRlYyMDGg0Gr2+DLmHWFhYiKSkJCQlJQEofxQlKSkJqampAMovt44bN067/uTJk3Hv3j189NFHuHnzJlavXo3ff/8dM2bMqM+Pxam84jKM33gOj/NK4ONshZ3v9cabvX2pcJJyOrcZSCPSyfv2F9h5uJ5wS1NHh1J9GVw8x48fb9Al2DfeeEPvIe8uXLiAwMBABAYGAgCioqIQGBiIefPmAQDS09O1hRQAfH19sXfvXsTExCAgIABff/01fvzxR5N7TIVhGHz8x2XcfVIED3sL/PZOT3T0oGECiQ5ra64jME86ed//Kb/ud5L6ETs4sNOOoRts2rTJoPXXrFmj97r9+vWr8eY/gGpHD+rXrx8SExMNiolvdiWl4eC1TEhEAqwf1x0e9uzfHyYmTq3mOgLzpJN3EUPHoElg6XeJtRGGTp48WenZSaKfUqUaS/ffBAD834C26NzcnuOICCGE1IW14jl48GA8fvyYrebMxpazqciQl8LD3gKTXmC/UxVpIt58k+sIzJNO3g8EDeIuDsIajU6/GWOwVjxru9xKqldcpsLq+LsAgPcHtKXOQaRma9dyHYF50sn7ipGm1xGRVKXRc+z0uvBqYHhzs/3CI2QXKuDtZIlXu9c+azkxc8860ZFGppP39Svf4TAQwhbxc8/VvZIeWCue69atqzJUHqndjoRHAIAJvXwhEdHfMaQWN25wHYF50sl7y6wHHAZCWHPzJivNsDY83+jRo9lqyizcyizAlcf5EAsFGN7Vk+twCCGEGMDg053Lly9Do9Hovf61a9egUqkM3U2T98ezs87+HVzhbFN1qEBCKunVi+sIzJNO3q+27MRhIIQtDEsj0BlcPAMDA5GTk6P3+iEhIZUGNiCASq3BzsTynskju9G9TqKHI0e4jsA86eQ96t0VHAZC2KI+dIiVdgy+bMswDObOnav3sHtlZWUGB9XUnbiTjawCBRytJBjQoekP9k1YMGoUsG0b11GYn1GjgOXlA73M+2UhMLM/xwERY4lGjwZqmJLMEAYXzxdeeAHJycl6rx8SEtIgM6qYsr8vpwMAXgrwhFRMHYWIHnbt4joC87Rrl7Z4Pn/9JMfBEDYI9uxhpR2Di2d8fDwrOzZXGg2D+OTy6cYiOrlzHA0hhJD6oNOeRnYjQ47sQgWspCIE+ThyHQ4xFS4uXEdgnnTynm9NQ2c2CSz9LlHxbGQVZ529WjtDJqYRhYieHj3iOgLzpJP3Vz/9ncNACFtUKSmstEPFs5EdvVVePPu2p45CxABz53IdgXnSyftbBzdyGAhhi3D+fHbaYaUVohd5qRIXHzwFAPRt24zjaIhJ+fJLriMwTzp5f+0o9XZuCoQr2HnkiIpnIzp1JxsqDYNWLtZo4azfoz6EEEL4h7XiWVJSUu2UZNeuXWNrFybv2O1sAMAL7eiskxBCTBkrxXPHjh1o27Ythg4dCn9/f5w9e1b73tixY9nYRZNwLiUXQHlnIUIMkpbGdQTmSSfvI+cY/2A94Z7qATsD/LNSPD///HMkJCQgKSkJmzZtwttvv40tW7YAoHk+K+QUKnAnqxAA8JyPE8fREJOzdy/XEZgnnbz3vHGGw0AIWwT79rHSDivFU6lUaqcjCwoKwrFjx7Bu3TosXLgQAoGAjV2YvPP3yzsKtXOzgaO1lONoiMmZNInrCMyTTt5n/vE1h4EQtoimTGGlHVaKp6urKy5fvqz93snJCTExMbhx40al5ebs/P3yS7Y9fOmskxBCTB0rxfPnn3+Gq2vl5xalUil+++03HD16lI1dmLyK+510yZYQQkwfK8XTwsICYnH1w+T27t2bjV2YtJIyNa6nywFQ8ST1RGNKc0Mn79Pf/Ya7OAhr1IcPs9JOvYtnXl4eIiMj4eLiAjc3N7i5ucHFxQVTp05FXl6eUUFFR0fDx8cHFhYWCA4Oxrlz52pdf+XKlWjfvj0sLS3h7e2NGTNmoLS01KgY2HQ9PR9qDYNmtjJ42FtwHQ4hhBAjGTyrCgDk5uYiJCQEjx8/xpgxY9CxY0cAwPXr17F582bExsbi1KlTcHQ0fODzbdu2ISoqCmvXrkVwcDBWrlyJiIgIJCcnV7k0DABbtmzBrFmzsHHjRvTq1Qu3bt3Cm2++CYFAgBUsjSRhrEsP8wEAAV721IGK1E+/fgCP/iA0G/36Acnlj6usXDcD+D6S23iI0UShoaz8LtWreC5cuBBSqRR3797V9rLVfS88PBwLFy7EN98YfpljxYoVmDRpEiZMmAAAWLt2Lfbu3YuNGzdi1qxZVdY/deoUevfujdGjRwMAfHx88Prrr1d61pRrlx/lAQC6NHfgNA5CCCHsqNdl27/++gtfffVVlcIJAO7u7li+fDl27txpcLtlZWVISEhAaGjoPwEKhQgNDcXp06er3aZXr15ISEjQXtq9d+8e9u3bhyFDhhi8/4Zy+XH5mae/N01pRAghTUG9zjzT09PRqVOnGt/v3LkzMjIyDG43OzsbarW6SlF2c3PDzZs3q91m9OjRyM7OxvPPPw+GYaBSqTB58mR88sknNe5HoVBAoVBov5fLyzvzKJVKKJVKg+OuTUGpEveeFAEA/NysWW+/sVXEb+o/R2NhK1+CNWvAmEHO+fb5EqxZA5VKDQD4cuQH+IgncVXgW774rCJHZd9/D2EN+TIkj/Uqni4uLrh//z68vLyqfT8lJQVOTo3TqzQ+Ph5ffPEFVq9ejeDgYNy5cwfTpk3DokWLMLeGaZyWLFmCBQsWVFkeFxcHKyt2B2y/lS8AIIKTjMGZo+z08uKDmJgYrkMwKcbmSyKRQMnSyCimgC+fL4lEglunTwEQI7alPzrz9BjwJV+mINbCosbfpeLiYr3bETD1GD/vrbfewt27dxETEwOptPJoOQqFAhEREWjVqhU2bjRs/ruysjJYWVlhx44dePnll7XLx48fj7y8POzatavKNn369EHPnj3xpc7UQb/88gveeecdFBYWQiisemW6ujNPb29vpKenw9mZ3XFn1x1LwVcxtzG4kxu+fS2A1ba5oFQqERMTg7CwMEgkEq7D4T228iW2tYWqoIDFyPiJb58vsa0tzl17iFHrzyH56xEQFhdxHVIlfMsXn1Xk6qVRo2r8XZLL5XBxcUF+fj7s7Oxqba/eHYa6d++Otm3bIjIyEh06dADDMLhx4wZWr14NhUKBn3/+2eB2pVIpgoKCEBsbqy2eGo0GsbGxmDp1arXbFBcXVymQIpEIQM3j6spkMshksirLJRIJ6x/Aa+nlB6lrC8cm9eFuiFw1ZWzky5zyzafPl1hc/v+JAPw9BnzKlymoKVeG5LBexdPLywunT5/Ge++9h9mzZ2uLlEAgQFhYGL7//nt4e3vXp2lERUVh/Pjx6N69O3r06IGVK1eiqKhI2/t23LhxaN68OZYsWQIAGDZsGFasWIHAwEDtZdu5c+di2LBh2iLKpcuPnnUW8nLgNhBCCCGsqVfxBABfX1/s378fT58+xe3btwEAbdq0Mfpe56hRo/DkyRPMmzcPGRkZ6Nq1Kw4cOKDtRJSamlrpTHPOnDkQCASYM2cOHj9+jGbNmmHYsGFYvHixUXGwIbtQgcd5JRAIgM7Na78EQEitZs7kOgLzpJP3rX1HYRyHoRB2aKKiwMZpVb2LZwVHR0f06NGDhVD+MXXq1Bov08b/a5gysViM+fPnY/78+azGwIaK5ztbuVjD1oIuqRAjLFrEdQTmadEi4EH5uNQbI96i4tkEaBYsYKV41us5zyNHjsDPz0/7iIeu/Px8dOrUCcePHzc6OFP3z8hCDtwGQkxfDT3bSQPTyfv2xf/lMBDCFrGvLyvt1Kt4rly5EpMmTaq2N5K9vT3effdd3gyNx6WKM09/LxocgRgpO5vrCMyTTt7ti/I5DISwhqXfpXoVz0uXLmHQoEE1vh8eHo6EhIR6B9VUXE0rPzPvQmeehBDSpNSreGZmZtbapVcsFuPJkyf1DqopeFKgwJMCBQQCoKOHLdfhEFM3fDjXEZgnnbyf8KPpFZsCZtgwVtqpV/Fs3rw5rl69WuP7ly9fhoeHR72DagpuPJu/08fZGlZSo/tlEXO3bRvXEZgnnbwvfGMeh4EQtqi3bGGlnXoVzyFDhmDu3LnVzplZUlKC+fPn48UXXzQ6OFNWUTz9POgRFcKCAQO4jsA86eR9xbooDgMhbBGFh7PSTr1OiebMmYM///wT7dq1w9SpU9G+fXsAwM2bNxEdHQ21Wo1PP/2UlQBNVUXxpEu2hBWnTnEdgXnSyXvnB9c4DISwRVDDDF2GqlfxdHNzw6lTpzBlypQqIwxFREQgOjq62unKzMmNZ8PydaQzT0IIaXLqfTOuZcuW2LdvH54+fYo7d+6AYRi0/f/27j0uqjr/H/hrhmFABEFFuYmCpBEqqKAsueqWKJuZmWbkqigqmwoFktfWULMNNENx9RdpIa2/Vdzy0s0sQqDVSOXiLRVFMSgFRERuCsPM+f6BjDMBysw58Dlw3s/Hg8dDDmc+58WbmT6dcz7n8xkwAN27dxcyX4d0X6VG3q0qANR5EoE89RTrBNKkU/dfe/fDAIZRiEDc3QVpRpAZhkaMGCFElk4jr6QKag0HGwtTOFibs45DOoOcHNYJpCknRzvDUEjEDqSxTUMEUH/qFISY782oAUPk0S403u+07waZTMY4DekUFi5knUCadOoeuX8zwyBEKPLQUGHaEaQVoufhYCG6ZEsEkpjIOoE06dT9r1lH2OUggpH/+9/CtGPoC86ePQuNRiPIwTurCw9mFvJwpM6TEEI6I4M7z2HDhqH0wdyA/fv3x+3btwUP1ZFxHIdLRY0jbekxFSIQEaxNK0k6dVfL6G/QKQj0WTK487SxsUF+fj4A4Pr163QW+gdFFfdx954KJnIZnuhtyToO6Syqq1knkCaduj/3z8MMgxCh1JeXC9KOwZ3ntGnTMHbsWLi6ukImk8HHxwf9+/dv9kuKGs86+9t2hZmC/k+VCIRWKWJDp+7Tf/yMYRAiFPmWLYK0Y/CjKjt27MDUqVORl5eHN954AyEhIbCyosuTjXIfdJ5P2lNNiIDeeguIpOnh2t1bbwHT5gIAQo58DOD/MY1D+JO//TawbBnvdox6zrNxObKsrCyEh4dT56mjsfN0p86TEEI6LV6TJOzatQvl5eX44IMPcPHiRQDAoEGDMG/ePFhbS3MB6EvaM08aaUsIIZ0Vr+c8MzMz4ebmhs2bN6OsrAxlZWWIjY2Fm5sbsrOzhcrYYajUGlwtaZiW70k7OvMkAvqFJiVnQqfuc99MYBiECKX+9GlB2uHVeS5ZsgSTJ0/G9evXceDAARw4cAD5+fmYNGkSIiIiBAnYkfx6uxp1ag0slCbo070L6zikM3lwZYe0M5269ysuYBiECEV26ZIg7fA+81yxYgUUiodXfxUKBZYvX47MzEze4Tqaxku2A+2sIJfTtHxEQC+/zDqBNOnUfd3/X8suBxGMyauvCtIOr86zW7duKCho+n9jhYWFkhxERIOFCCFEGnh1noGBgZg/fz727duHwsJCFBYWIikpCQsWLMCMGTOEythhXKLHVAghRBJ4dZ6bNm3C1KlTERQUBBcXF7i4uGDu3Ll4+eWXsWHDBqPb3b59O1xcXGBubg5fX1+cPHnykfuXl5cjNDQUDg4OMDMzw8CBA3H4cPvPBqJ9xpMGCxGhHTzIOoE06dR9ddA7DIMQoag/E2ayC16PqiiVSsTFxSE6OhpXr14FALi5ucHCwsLoNvft24fIyEjEx8fD19cXW7ZsQUBAAHJzc9G7d+8m+9fV1WH8+PHo3bs3Pv/8czg5OeHXX3+FjY2N0RmMUVNXj4KyGgB05knagJsb6wTSpFP3Gz0dGQYhQuEEmv1OkCXJLCwsMGTIEAwZMoRXxwkAsbGxCAkJQXBwMDw8PBAfHw8LCwskJDQ/TDwhIQFlZWU4dOgQRo0aBRcXF4wdOxZeXl68chjqcnHDIyq2lmboaWnWrscmEuDpyTqBNOnUPWHzAoZBiFAU3t7CtCNIKwKpq6tDVlYWVq1apd0ml8vh7++PjIyMZl/z5Zdfws/PD6Ghofjiiy/Qq1cv/O1vf8OKFStg0sLs+bW1taitrdV+X1HRsISYSqWCSqUyKvuF3+8AAAbadTW6jY6g8XfrzL+jkISqlwJAvQRqLrb3lwJAfb0aAMBBPLkaia1eYqZbo5bqZUgdRdV5lpaWQq1Ww87OTm+7nZ0dLrXwbM61a9dw9OhRzJw5E4cPH0ZeXh4WL14MlUqFNWvWNPua6OhorFu3rsn21NRUo8+cv8uXA5BDWVPK5H5re0tOTmYdoUPhW69JGo0k3leNxPL+mqTRICPjJwAKgINo/wZiqVdHoHnEZ6mmpqbV7Yiq8zSGRqNB7969sWPHDpiYmMDb2xu///473n///RY7z1WrViFSZ5LtiooKODs745lnnkHPnj2NypG0KxNAGQL+NAQThzsZ1UZHoFKpkJycjPHjx8PU1JR1HNETrF4xMZg4caJwwURKdO+vmBj4+T2NLedP4qOJf8cikf0NRFcvEWuslfqf/2zxs9R4FbI1RNV52trawsTEBMXFxXrbi4uLYW9v3+xrHBwcYGpqqneJ9qmnnkJRURHq6uqgVCqbvMbMzAxmZk3vS5qamhr9BrzyYFo+D0cbSbyJ+dRKinjXKzISUlrgTjTvr8hIKH4tAwAcGD0Nb4ghUzNEU68OQBYR0WKtDKmhIAOGhKJUKuHt7Y2UlBTtNo1Gg5SUFPj5+TX7mlGjRiEvL09vUe7Lly/DwcGh2Y6zLZRW1aK0qg4yWcPsQoQIztycdQJp0qn7928FMAxChKIQaAIf3meeKSkpSElJQUlJiV4HBqDFEbKPEhkZiTlz5sDHxwcjR47Eli1bUF1djeDgYABAUFAQnJycEB0dDQBYtGgRtm3bhvDwcLz++uu4cuUK3nvvPbzxxht8f7VWa3y+s18PC3RRSun8gBBCpIlX57lu3Tq888478PHxgYODA2Qy/vO5BgYG4tatW4iKikJRURGGDh2KI0eOaAcRFRQUQC5/eMLs7OyM7777DkuWLIGnpyecnJwQHh6OFStW8M7SWjSzECGESAuvzjM+Ph6JiYmYPXu2UHkAAGFhYQgLC2v2Z2lpaU22+fn54eeffxY0gyFyixpuMtManqTNzJ/POoE06dT9mxET8RLDKEQYmuBgQcYP8LrnWVdXh6efflqAGB0bTQhP2tz27awTSJNO3eNeCmcYhAhFs3WrIO3w6jwXLFiAPXv2CBKko1JrOO3sQtR5kjYzZAjrBNKkU/eEWDr77wwUw4YJ0w6fF9+/fx87duzADz/8AE9PzybDfGNjY3mF6wgKympwT6WGuakc/Xp2ZR2HdFZXrrBOIE06de9T+hvDIEQweXmCNMOr8zx79iyGDh0KADh//rzez4QYPNQRXLrZcL9zoJ0VTGgBbEIIkQRenWdqaqpQOTqsS3S/k7SHMWNYJ5AmnbqfcfXECIZRiDC40aMhxGkO7+c8y8vL8cknn+DixYsAgEGDBmHevHmwtrbmHa4juEQjbUl7+P571gmk6fvvgQczDC0LeR9pbNMQAagPHxZkdiBebWRmZsLNzQ2bN29GWVkZysrKEBsbCzc3N2RnZwsQT/wazzyfojNP0pamTWOdQJp06r7u383PlU06FpNXXhGkHV6d55IlSzB58mRcv34dBw4cwIEDB5Cfn49JkyYhIiJCkIBiVl1LC2CTdvLNN6wTSJNO3f0usXuWnAhH9u23grTD67JtZmYmdu7cCYXiYTMKhQLLly+Hj48P73Bid7m4EhwH9LKiBbAJIURKeJ15duvWDQUFBU22FxYWwkqgyXfFjAYLkXbTwqpCpI3p1L3MqgfDIEQwAn2WeHWegYGBmD9/Pvbt24fCwkIUFhYiKSkJCxYswIwZMwQJKGaNMws95UCDhUgbu36ddQJp0qn7q6v2sstBBFMv0DPTvDrPTZs2YerUqQgKCoKLiwtcXFwwd+5cvPzyy9iwYYMgAcXs4oNnPJ+kZchIW1u1inUCadKpe8i3OxkGIUKR/+MfwrTD58VKpRJxcXG4c+cOTp8+jdOnT6OsrAybN29udrHpzoTjuIeXbR2o8yRtbPNm1gmkSafu0//3OcMgRChygea25f2cJwBYWFhgiMTm3iyuqMXdeyqYyGV4orcl6ziEEELakcGdZ2RkJNavX4+uXbsiMjLykft25rltLz6YHKG/bVeYKWgBbEIIkRKDO8+cnByoVCrtv1vS2ee2vXSz8ZItDRYi7aCkhHUCaSopAe7UAwCmRB3AYcZxCH/1v/8O08fv9lgGd56689l++umn6NOnD+Ry/VunHMehsLCQfzoRa1wAmx5TIe1i/34gOJh1CunZvx949kUAwJhzPwKYxDYP4U128CCwYAHvdngNGHJ1dUVpaWmT7WVlZXB1deXTtOjRM56kXS1axDqBNOnUPfLgFnY5iGBMwsIEaYdX58lxXLPbq6qqYG5uzqdpUaur1yCv5MEC2HTZlhBCJMeo0baNA4VkMhmioqJgYWGh/ZlarcaJEye063x2RtdKq1Cv4WBlroCjdef9nwRCCCHNM6rzbBwoxHEczp07B6VSqf2ZUqmEl5cXli5dKkxCEdIOFrK36vQDo4hIHDvGOoE06dT99UVb8RHDKEQY6rQ0QZ7RNKqNxkFDwcHBiIuLQ7du0rp0eVE7WEhavzdhqLaWdQJp0qm7qVrFMAgRjECfJV73PHft2iW5jhN4OKctzSxE2s24cawTSJNO3WN3vMkwCBGKSUCAIO3w6jyjo6ORkJDQZHtCQgKvuW23b98OFxcXmJubw9fXFydPnmzV65KSkiCTyTBlyhSjj90aupdtCSGESA+vzvOjjz6Cu7t7k+2DBg1CfHy8UW3u27cPkZGRWLNmDbKzs+Hl5YWAgACUPOYh8evXr2Pp0qUYPXq0UcdtrfKaOhRV3AcADKQJ4QkhRJJ4dZ5FRUVwcHBosr1Xr164efOmUW3GxsYiJCQEwcHB8PDwQHx8PCwsLJo9w22kVqsxc+ZMrFu3Dv379zfquK3V+Hync48usDIXYp4KQlrhEe9/0oZ06r5h+nKGQYhQ1Dt2CNIOr87T2dkZx48fb7L9+PHjcHR0NLi9uro6ZGVlwd/f/2FAuRz+/v7IyMho8XXvvPMOevfujfnz5xt8TEM9XIZMevd6CUPjx7NOIE06dc8c4M0wCBEKp9O/8MFrxG5ISAgiIiKgUqnw7LPPAgBSUlKwfPlyvPmm4TfXS0tLoVarYWdnp7fdzs4Oly5davY1x44dwyeffILTp0+3+ji1tbWo1RlxVVHR0CGqVCrtvL0tOfdbOQDgKfuuj923M2r8naX4uxtDqHopnJ1RX1kpRCRRE9v7S+HsjPpfGqYa/e97gVCtrmacSJ/Y6iVmjTVS9O8PVQufJUPqyKvzXLZsGW7fvo3Fixejrq4OAGBubo4VK1ZgVTss3ltZWYnZs2dj586dsLW1bfXroqOjsW7duibbU1NT9SZ8aM6JXBMAMty7cQWHD182NHKnkZyczDpCh8K3XpM0Ghw+LJ1pycXy/pqk0SAj4ycACoCDaP8GYqlXR6B5xGeppqam1e3IuJbm2DNAVVUVLl68iC5dumDAgAFGL4RdV1cHCwsLfP7553ojZufMmYPy8nJ88cUXevufPn0aw4YNg4nJwyXBNBoNgIbLvbm5uXBzc2tynObOPJ2dnXHz5k307NmzxXy19RoMXZ+Ceg2H9DdHw9Gmi1G/Z0emUqmQnJyM8ePHw9SU7vk+jlD1UlhZSebMU0zvL4WVFU7+UojAnSeR+8FLkNeI78xTTPUSs8ZaTQ4MbPGzVFFRAVtbW9y9e/exj2EKshi2paUlRowYwbsdpVIJb29vpKSkaDtPjUaDlJQUhDUzma+7uzvOnTunt2316tWorKxEXFwcnJ2dmz2OmZlZsx28qanpI9+Al4rvol7DobuFKfraSnt2ocfViujjXa+VKyVVb9G8v1auhOLBer17/zIDc8WQqRmiqVcHoFm2rMVaGVJDQTrPCxcuoKCgQHvpttHkyZMNbisyMhJz5syBj48PRo4ciS1btqC6uhrBD5ZjCgoKgpOTE6Kjo2Fubo7Bgwfrvd7GxgYAmmwXwi837gIABjlaS7rjJAysXcs6gTStXQv8WgYASJwwF3OZhiFC0ERFweTxuz0Wr87z2rVreOmll3Du3DnIZDLtKiuNHYtarTa4zcDAQNy6dQtRUVEoKirC0KFDceTIEe0gooKCgibrh7aX89rOk0baknbm4AAY+fgX4cHBAfj5FwDA/vUvA8tuMw5E+FL07SvIZ4lX5xkeHg5XV1ekpKTA1dUVJ0+exO3bt/Hmm29i06ZNRrcbFhbW7GVaAEhLS3vkaxMTE40+7uP8cqNhVO4gJ+s2OwYhzbpzh3UCadKpu9W9zn/PWRIE+izx6jwzMjJw9OhR2NraQi6XQy6X489//jOio6PxxhtvaFdf6QzUGk77jCedeRJCiLTxuv6pVqthZdUwRZ2trS1u3LgBAOjXrx9yc3P5pxORa7eqcF+lgYXSBK49u7KOQ6Rm6lTWCaRJp+4/Dm7bqT9J++AEmvuc15nn4MGDcebMGbi6usLX1xcbN26EUqnEjh072nyavPbWeMnWw6Eb5HIaLETa2Z49rBNI05492gFD7/5tNYSZm4awpN69m99Z4wO82li9erV2kNA777yD/Px8jB49GocPH8bWrVsFiCceZ39rGCw0mO53EhbGjmWdQJp06r7lwwh2OYhgTARa3s/oM0+VSoWNGzdqV0954okncOnSJZSVlaF79+6d7lGOc7+XAwA8+1DnSRg4cYJ1AmnSqbtH4UWGQYhQZK1c4vJxjO48TU1Ncfbs2Sbbe/TowSuQGNWrNTj/e8NlW+o8CSGE8LpsO2vWLHzyySdCZRGtq7eqcU+lRlelCfrbWrKOQ6SoDSb9IK2gU/d8O1eGQYhQuEGDBGmH14Ch+vp6JCQk4IcffoC3tze6dtUfhRobG8srnFicebCSymAnaxosRNjIzGSdQJoyM7UDhl4Lj0ca2zREAOqff2Y/YOj8+fMYPnw4rKyscPnyZeTk5Gi/DFkiTOzOPug8vZxtmOYgEhYSwjqBNOnUfennxk/8QsTDZOFCQdrhdeaZmpoqSAixO1PYMNJ2CI20Jazs3g3s3Mk6hfTs3g2s3gAAmJBNy351BrL//AcQ4HajUWee165dgwArmXUINXX1uPBgZqHh/bozTkMIIUQMjOo8BwwYgFu3bmm/DwwMRHFxsWChxOTsb3eh1nCw72YOR2tz1nGIVCmVrBNIk07dVSa05FenINBnyajO849nnYcPH0Z1tbgWiRVK1q8Nkwh79+t8z66SDqSignUCadKp+/Prv2YYhAil/rYwK+OwWdurA8l+0HnSJVvC1MaNrBNIk07dX01LYhiECEXOY8UvvXaMeZFMJmtyFtYZz8o4jkNWwcMzT0KYiYpinUCadOo+7/tdDIMQocjXrROkHaNG23Ich7lz58LMzAwAcP/+fSxcuLDJc54HDhzgn5Cha6XVKK9RwUwhh4cDLUNGCCGkgVGd55w5c/S+nzVrliBhxKbxfqdXHxsoFXSFmxBCSAOjOs9du6Rx+YLudxLR6GTr43YYOnWfvexT7GMYhQij/vx5CDFumk6nHkF3pC0hTOXksE4gTTp1H/B7HsMgRCgygWa/o86zBXdrVLhSUgUAGN7Xhm0YQl59lXUCadKpe9Se9QyDEKGYCHSbkTrPFmQXNpx1utp2RU9LM8ZpCCGEiAl1ni3Q3u/sS5dsCSGE6KPOswUn8huWIaL7nUQUvvqKdQJp0qn7qrn/ZBiECEV98KAg7fDuPP/3v/9h1qxZ8PPzw++//w4A2L17N44dO8Y7HCs1dfXIeTA5wtNuPRmnIQSAkxPrBNKkU/dSa1uGQYhQOEdHQdrh1Xnu378fAQEB6NKlC3JyclBbWwsAuHv3Lt577z2j292+fTtcXFxgbm4OX19fnDx5ssV9d+7cidGjR6N79+7o3r07/P39H7l/a5y6fgcqNQcnmy7o19OCV1uECGL4cNYJpEmn7jvjXmMYhAhF4esrSDu8Os93330X8fHx2LlzJ0xNHz45M2rUKGRnZxvV5r59+xAZGYk1a9YgOzsbXl5eCAgIQElJSbP7p6WlYcaMGUhNTUVGRgacnZ0xYcIE7VmwMX7KKwXQcNbZGacdJIQQwg+vzjM3Nxdjxoxpst3a2hrl5eVGtRkbG4uQkBAEBwfDw8MD8fHxsLCwQEJCQrP7/+c//8HixYsxdOhQuLu74+OPP4ZGo0FKSopRxweA41cbOs9RT9BlGkIIIU0ZNcNQI3t7e+Tl5cHFxUVv+7Fjx9C/f3+D26urq0NWVhZWrVql3SaXy+Hv74+MjIxWtVFTUwOVSoUePXq0uE9tba32EjMAVDxYdkilUuHW3Rr8cqPh+xH9rKFSqQz+PTqzxnpQXVpHqHrJN26ERgI1F9v7S75xI+rr1QCA7c8vQphIcjUSW73ETFur6GjIWqiXIXXk1XmGhIQgPDwcCQkJkMlkuHHjBjIyMrB06VK8/fbbBrdXWloKtVoNOzs7ve12dna4dOlSq9pYsWIFHB0d4e/v3+I+0dHRWNfMzPqpqam4cq8rOM4E9l04ZP7P+LPXzi45OZl1hA6Fb71kffqAO3xYoDTiJ5b3l6xPH1zL+AmAAkmez6K/SP8GYqlXR3CkX78WP0s1NTWtbodX57ly5UpoNBqMGzcONTU1GDNmDMzMzLB06VK8/vrrfJo2SkxMDJKSkpCWlgZzc/MW91u1ahUiIyO131dUVMDZ2RnPPPMMzv9UAqAQ4736YeJE93ZI3bGoVCokJydj/Pjxeve5SfOEqpfCygr1lZUCJhMnsb2/FFZWOPlLIbacP4kfo6dDXlPNOpIesdVLzBpr9cLf/tbiZ6nCgEXneXWeMpkM//jHP7Bs2TLk5eWhqqoKHh4esLS0NKo9W1tbmJiYoLi4WG97cXEx7O3tH/naTZs2ISYmBj/88AM8PT0fua+ZmZl2OTVdCoUCx682rDL+5wG96M34CKamplQfAwhRLynVW0zvL4XCBAAgg3j/BmKqV0fQUq0MqaEgkyQolUp4eHhg5MiRRnecje14e3vrDfZpHPzj5+fX4us2btyI9evX48iRI/Dx8TH6+Ndv1+D67RooTeQ0WIgQQkiLDD7z1L3c+TixsbGGNo/IyEjMmTMHPj4+GDlyJLZs2YLq6moEBwcDAIKCguDk5ITo6GgAwIYNGxAVFYU9e/bAxcUFRUVFAABLS0uDO/L0Kw2jbP/k1hOWZrxOygkR1mv0jCETOnX/0vcFvMwwChGGJiQEJgK0Y3APkdPKpZGMfT4yMDAQt27dQlRUFIqKijB06FAcOXJEO4iooKAAcvnDE+YPP/wQdXV1ePll/bf1mjVrsHbtWoOO/ePlhs7T/6neRmUnpM3ExbFOIE1xccCvDVN1bnsxjDrPTkATG8um80xNTRXgsI8WFhaGsLCwZn+Wlpam9/3169cFO+6Z3+4CSgs8606dJxEZDw/gwgXWKaTHwwP4tmGq0cRNwcCy62zzEN4Unp7AxYu826GJ4XVoOOAph27o052m5CMic+0a6wTSpFN3x7IbDIMQweTnC9IM7xt75eXl+OSTT3DxQU/u4eGB+fPnw9ramnc4FuiSLSGEkMfhdeaZmZkJNzc3bN68GWVlZSgrK8PmzZvh5uZm9Ny2rPk/Zff4nQhpb888wzqBNOnUPcdtGMMgRCjcX/4iSDu8Os8lS5Zg8uTJuH79Og4cOIADBw4gPz8fkyZNQkREhCAB21Of7ubw7NMxz5hJJ/ftt6wTSJNO3VfMj2EYhAhFLdDauLzPPFesWAGF4uHVX4VCgeXLlyMzM5N3uPb210H2tIoKEacpU1gnkCadur/7qeFTjhLxMZk2TZB2eHWe3bp1Q0FBQZPthYWFsLKy4tM0E38dRPc7iUgdOcI6gTTp1H1kLr91ggl7ag0g+/57qDUc77Z4dZ6BgYGYP38+9u3bh8LCQhQWFiIpKQkLFizAjBkzeIdrb269jJ8diRBCiLjtvy5HXb0G/zp6hXdbvEbbbtq0CTKZDEFBQaivrwfHcVAqlVi0aBFiYuj+ACGCcXJinUCadOp+q5st6K/QsXEAiqx6Qi7A7TleZ55KpRJxcXG4c+cOTp8+jTNnzmhH3DY38TohxEhXr7JOIE06dZ+58j8MgxAhcBww9rWPIcTIFqPOPOfNm9eq/RISEoxpnhDyR8uXAxs3sk4hPcuXA6ErAQCvffMRsIweGeroVqYmQB2wgXc7Rp15JiYmIjU1FeXl5bhz506LX4QQgWzdyjqBNOnUfdrxAwyDECFoOCA46ysI8VCFUWeeixYtwt69e5Gfn4/g4GDMmjULPXr04J+GEEIIaSONY2xlAly4NerMc/v27bh58yaWL1+Or776Cs7OznjllVfw3XffgeP4DwEmhBBC2opcgDNPowcMmZmZYcaMGUhOTsaFCxcwaNAgLF68GC4uLqiqquKfjBDy0O3brBNIk07dJ689xC4HEQTHAUPf2CvIZVtBVlWRy+WQyWTgOA5qtVqIJgkhuvbuZZ1AmnTqPi7nKMMgRAgaAJMvprN9VKW2thZ79+7F+PHjMXDgQJw7dw7btm1DQUEBLC1psgFCBNXC+rakjenUPfwLGrTVGbyTHC/INKxGDRhavHgxkpKS4OzsjHnz5mHv3r2wtbXlHYYQQghpK41Dcpg95xkfH4++ffuif//+SE9PR3p6erP7HThAQ7sJIYSIQ+NwViEGDBnVeQYFBdHqI4S0p4wM1gmkSafui0O34WOGUQh/HICpszfhFVaXbRMTE3kfmBBigMpK1gmkqbISePAIu0VtDdsshDeOA7rW3WP7qAohpB1NmMA6gTTp1H3Tx8sZBiFC4ADs3ve2IFdOqfMkhBAiCdoBQ3TmSQghhLTOwwFDnfTMc/v27XBxcYG5uTl8fX1x8uSjV3D/7LPP4O7uDnNzcwwZMgSHDx9up6SEtJNPP2WdQJp06h79ykqGQYhQ3nx+CbtHVdrSvn37EBkZifj4ePj6+mLLli0ICAhAbm4uevfu3WT/n376CTNmzEB0dDQmTZqEPXv2YMqUKcjOzsbgwYMZ/AaEtIExY1gnkKYxY4C6hn+m9HJH4aen2Ob5A42GQ0mJHF+U5UAuxCiYTkyj4XC9SoZi58EYw2q0bVuKjY1FSEgIgoODATQ8U/rNN98gISEBK1c2/T+/uLg4/PWvf8WyZcsAAOvXr0dycjK2bduG+Pj4ds1OSJtxdQXu32edQnpcXdHzt4b5bZO3zcWTSw8yDtQcOXDnFusQHYQMWfHz8VPYb7xbElXnWVdXh6ysLKxatUq7TS6Xw9/fHxktPOeWkZGByMhIvW0BAQE4dOhQW0YlhEiEi21XJP39T1BslmPDtCGs4+hRq9U4e/YcPD2HwMTEhHUcUWuslcJEjjEDe/FuT1SdZ2lpKdRqNezs7PS229nZ4dKlS82+pqioqNn9i4qKWjxObW0tamtrtd/fvXsXAFBWVmZsdMlQqVSoqanB7du3YWpqyjqO6AlVLwXHoV4CK6uI7f3VWPcB1kAVOPj378o6kh6VSgVZfjWecekiinqJWWOtqsCh/k7z/62vfPA8dWuW1hRV59leoqOjsW7duibbBw4cyCANIa1E80ezoVt3+ht0Do/5O1ZWVsLa2vqR+4iq87S1tYWJiQmKi4v1thcXF8Pe3r7Z19jb2xu0PwCsWrVK71JveXk5+vXrh4KCgscWTOoqKirg7OyMwsJCdOvWjXUc0aN6GYbqZRiqV+u1plYcx6GyshKOjo6PbU9UnadSqYS3tzdSUlIwZcoUAIBGo0FKSgrCWliSyc/PDykpKYiIiNBuS05Ohp+fX4vHMTMzg5mZWZPt1tbW9AZspW7dulGtDED1MgzVyzBUr9Z7XK1aewIlqs4TACIjIzFnzhz4+Phg5MiR2LJlC6qrq7Wjb4OCguDk5ITo6GgAQHh4OMaOHYsPPvgAzz//PJKSkpCZmYkdO3aw/DUIIYR0YqLrPAMDA3Hr1i1ERUWhqKgIQ4cOxZEjR7SDggoKCiCXP5zb4emnn8aePXuwevVqvPXWWxgwYAAOHTpEz3gSQghpM6LrPAEgLCysxcu0aWlpTbZNnz4d06dPN/p4ZmZmWLNmTbOXcok+qpVhqF6GoXoZhurVekLXSsa1ZkwuIYQQQrREObctIYQQImbUeRJCCCEGos6TEEIIMRB1noQQQoiBJN95Grp2qFT8+OOPeOGFF+Do6AiZTNZkon2O4xAVFQUHBwd06dIF/v7+uHLlCpuwjEVHR2PEiBGwsrJC7969MWXKFOTm5urtc//+fYSGhqJnz56wtLTEtGnTmsyMJRUffvghPD09tQ+r+/n54dtvv9X+nGrVspiYGMhkMr1JYahe+tauXQuZTKb35e7urv25UPWSdOfZuHbomjVrkJ2dDS8vLwQEBKCkpIR1NOaqq6vh5eWF7du3N/vzjRs3YuvWrYiPj8eJEyfQtWtXBAQE4L4El81KT09HaGgofv75ZyQnJ0OlUmHChAmorq7W7rNkyRJ89dVX+Oyzz5Ceno4bN25g6tSpDFOz06dPH8TExCArKwuZmZl49tln8eKLL+KXX34BQLVqyalTp/DRRx/B09NTbzvVq6lBgwbh5s2b2q9jx45pfyZYvTgJGzlyJBcaGqr9Xq1Wc46Ojlx0dDTDVOIDgDt48KD2e41Gw9nb23Pvv/++dlt5eTlnZmbG7d27l0FCcSkpKeEAcOnp6RzHNdTG1NSU++yzz7T7XLx4kQPAZWRksIopKt27d+c+/vhjqlULKisruQEDBnDJycnc2LFjufDwcI7j6L3VnDVr1nBeXl7N/kzIekn2zLNx7VB/f3/ttsetHUoa5Ofno6ioSK921tbW8PX1pdrh4RJ3PXr0AABkZWVBpVLp1cvd3R19+/aVfL3UajWSkpJQXV0NPz8/qlULQkND8fzzz+vVBaD3VkuuXLkCR0dH9O/fHzNnzkRBQQEAYeslyhmG2oMxa4eSBo1rpRq6jqoUaDQaREREYNSoUdopIouKiqBUKmFjY6O3r5Trde7cOfj5+eH+/fuwtLTEwYMH4eHhgdOnT1Ot/iApKQnZ2dk4depUk5/Re6spX19fJCYm4sknn8TNmzexbt06jB49GufPnxe0XpLtPAlpC6GhoTh//rzePRbS1JNPPonTp0/j7t27+PzzzzFnzhykp6ezjiU6hYWFCA8PR3JyMszNzVnH6RCee+457b89PT3h6+uLfv364b///S+6dOki2HEke9nWmLVDSYPG+lDt9IWFheHrr79Gamoq+vTpo91ub2+Puro6lJeX6+0v5XoplUo88cQT8Pb2RnR0NLy8vBAXF0e1+oOsrCyUlJRg+PDhUCgUUCgUSE9Px9atW6FQKGBnZ0f1egwbGxsMHDgQeXl5gr6/JNt56q4d2qhx7dBHrQVKAFdXV9jb2+vVrqKiAidOnJBk7TiOQ1hYGA4ePIijR4/C1dVV7+fe3t4wNTXVq1dubi4KCgokWa/maDQa1NbWUq3+YNy4cTh37hxOnz6t/fLx8cHMmTO1/6Z6PVpVVRWuXr0KBwcHYd9fPAY1dXhJSUmcmZkZl5iYyF24cIH7+9//ztnY2HBFRUWsozFXWVnJ5eTkcDk5ORwALjY2lsvJyeF+/fVXjuM4LiYmhrOxseG++OIL7uzZs9yLL77Iubq6cvfu3WOcvP0tWrSIs7a25tLS0ribN29qv2pqarT7LFy4kOvbty939OhRLjMzk/Pz8+P8/PwYpmZn5cqVXHp6Opefn8+dPXuWW7lyJSeTybjvv/+e4ziq1ePojrblOKrXH7355ptcWloal5+fzx0/fpzz9/fnbG1tuZKSEo7jhKuXpDtPjuO4f/3rX1zfvn05pVLJjRw5kvv5559ZRxKF1NRUDkCTrzlz5nAc1/C4yttvv83Z2dlxZmZm3Lhx47jc3Fy2oRlprk4AuF27dmn3uXfvHrd48WKue/funIWFBffSSy9xN2/eZBeaoXnz5nH9+vXjlEol16tXL27cuHHajpPjqFaP88fOk+qlLzAwkHNwcOCUSiXn5OTEBQYGcnl5edqfC1UvWpKMEEIIMZBk73kSQgghxqLOkxBCCDEQdZ6EEEKIgajzJIQQQgxEnSchhBBiIOo8CSGEEANR50kIIYQYiDpPQkRq7ty5mDJlSrsfNzExETKZDDKZDBEREbzb+uMKFo8zd+5c7fEPHTrE6/iEtBVaVYUQBmQy2SN/vmbNGsTFxYHVHCbdunVDbm4uunbtyqudwMBATJw40aDXxMXFISYmBg4ODryOTUhbos6TEAZu3ryp/fe+ffsQFRWF3Nxc7TZLS0tYWlqyiAagoXMXYlWOLl26GLwMlLW1NaytrXkfm5C2RJdtCWHA3t5e+2Vtba3trBq/LC0tm1y2/ctf/oLXX38dERER6N69O+zs7LBz505UV1cjODgYVlZWeOKJJ/Dtt9/qHev8+fN47rnnYGlpCTs7O8yePRulpaUGZ3ZxccG7776LoKAgWFpaol+/fvjyyy9x69YtvPjii7C0tISnpycyMzO1r/njZdu1a9di6NCh2L17N1xcXGBtbY1XX30VlZWVBuchhCXqPAnpQD799FPY2tri5MmTeP3117Fo0SJMnz4dTz/9NLKzszFhwgTMnj0bNTU1AIDy8nI8++yzGDZsGDIzM3HkyBEUFxfjlVdeMer4mzdvxqhRo5CTk4Pnn38es2fPRlBQEGbNmoXs7Gy4ubkhKCjokZebr169ikOHDuHrr7/G119/jfT0dMTExBiVhxBWqPMkpAPx8vLC6tWrMWDAAKxatQrm5uawtbVFSEgIBgwYgKioKNy+fRtnz54FAGzbtg3Dhg3De++9B3d3dwwbNgwJCQlITU3F5cuXDT7+xIkT8dprr2mPVVFRgREjRmD69OkYOHAgVqxYgYsXLzZZKF2XRqNBYmIiBg8ejNGjR2P27Nl66ysS0hHQPU9COhBPT0/tv01MTNCzZ08MGTJEu83Ozg4AUFJSAgA4c+YMUlNTm71/evXqVQwcONDo4zceq6Xjt3TP1MXFBVZWVtrvHRwctHkJ6Sio8ySkAzE1NdX7XiaT6W1rHMWr0WgAAFVVVXjhhRewYcOGJm0ZM5q1uWM96viPa6PxNY/anxAxos6TkE5s+PDh2L9/P1xcXKBQ0MedEKHQPU9COrHQ0FCUlZVhxowZOHXqFK5evYrvvvsOwcHBUKvVrOMR0mFR50lIJ+bo6Ijjx49DrVZjwoQJGDJkCCIiImBjYwO5nD7+hBhLxrGawoQQIkqJiYmIiIhAeXk50xwymQwHDx5kMkUhIY9D/+tJCGni7t27sLS0xIoVK9r92AsXLmQ6uxIhrUFnnoQQPZWVldrnNG1sbGBra9uuxy8pKUFFRQWAhhHBfOfXJaQtUOdJCCGEGIgu2xJCCCEGos6TEEIIMRB1noQQQoiBqPMkhBBCDESdJyGEEGIg6jwJIYQQA1HnSQghhBiIOk9CCCHEQNR5EkIIIQb6P/ZUCsZgztkwAAAAAElFTkSuQmCC",
- "text/plain": [
- "