# E S Fraga: Jacaranda blog

## Table of Contents

## Introduction

This document presents a collection of notes and news items for the Jacaranda system for automated design and optimisation sorted in reverse chronological order.

Visit my home pages for links to my pages on general research, teaching and other interests.

## Direct flowsheet definition

Although Jacaranda was explicitly created for the automated design of processes, including heat exchanger networks, there are times when one may wish to investigate the behaviour of a particular given process. Jacaranda provides a `Flowsheet`

class for just this purpose. An instance of this class can be defined directly by the engineer or as the result of an automated design run in Jacaranda. This blog entry will focus on the user defined case.

A flowsheet consists of a set of units and their connections. Connections are represented by *streams*. To define a flowsheet, we first define the units and the streams and finally tell Jacaranda how they come together.

The following is an example of a superstructure flowsheet. A superstructure is a representation of multiple structures and is the basis for alternative automated design procedures, often through the use of generic MINLP solvers such as those available through GAMS. Jacaranda also has a range of MINLP solvers, mostly based on stochastic optimisation methods but also direct search methods.

The superstructure consists of three distillation units. A valid process in this example could consist of just two distillation units but a three distillation unit process is actually less expensive to operate under specific conditions. It is worth exploring the trade-offs between the two options through the use of a single superstructure `Flowsheet`

object.

We consider the separation of a mixture which is actually the output of a reaction unit used for the chlorination of benzene. The feed to the separation section is 97% Benzene (component `A`

below), 1% C_{6}H_{5}Cl (`B`

), 1% p-C_{6}H_{4}Cl_{2} (`C`

), and 1% C_{6}H_{3}Cl_{3} (`D`

). The aim is produce pure Benzene (>98% purity) and somewhat pure C_{6}H_{5}Cl (>90% purity). The other two components are waste products and come out with any purity. We want less than 1% of any of the waste products in the Benzene output stream. Short cut design methods based on the Fenske-Underwood-Gilliland model.

The rest of this blog entry is a description of the input file for this process. We start with some general settings:

# start by specifying that all objects come from the Jacaranda # package, specifically the process synthesis sub-package and the unit # models. We turn on assertions to catch any silly errors as we are # still debugging some of this code use jacaranda.design.ps use jacaranda.design.units # define some global variables variables # the discretization of component flows const Base 0.001 "Base component flow discretization" # define the flows of the components in the feed const Fbenzene 0.97 "Flow of Benzene in feed stream" const Fc6h5cl 0.01 "Flow of C6H5Cl in feed stream" const Fpc6h4cl2 0.01 "Flow of p-C6H4Cl2 in feed stream" const Fc6h3cl3 0.01 "Flow of C6H3Cl3 in feed stream" # set the cost base to 1993 based on the M&S index set msindex 964.2 # and the number of discrete enthalpy (or state) levels for streams. # it is probably a good idea to have this value match the number of # operating pressures in the distillation columns. const nPs 4 "Number of discrete stream enthalpy levels" end # define the ranking criteria: we want to study the difference in # operating versus capital costs so we define two criteria, each of # which is the sum of the relevant criterion value generated by each # unit in a flowsheet. The term in quotes for each criterion is the # expression to evaluate for each unit. The "sum" means that the value # of the criterion is the sum of expression evaluate for all the units # in the resulting flowsheets. Criteria costs criterion CapitalCost sum "capcost" criterion OperatingCost sum "opercost" end # define the heat exchanger model for heating and cooling. We define # two cost functions, one for the capital cost as a function of the # area of the exchanger, and the other the operating cost as a # function of the duty (Q [kJ]), the cost of the utility used (Cu # [$/kJ]), and the time (seconds in a year equal to h*hpy where hpy is # the "hours per year"). To cater for equal temperature differences at # both ends of the exchanger, we use the Chen approximation. HeatExchanger hex model deltain = abs(T1in-T2out) deltaout = abs(T1out-T2in) eps = abs(deltain/deltaout-1) if eps < 4e-4 lmtd = (1+eps/2-eps^2/12)*deltaout else lmtd = (deltain - deltaout)/log(deltain/deltaout) endif A = Q/U/lmtd capcost = 101964 + 92.9*A^0.65 opercost = Cu*Q*h*hpy end end # define the utilities available for meeting heating and cooling # demands from the units. the parameters for each unit are inlet # temperature, outlet temperature, heat transfer coefficient, and # cost. DiscreteUtilities utils hot "Steam @ 28.23 atm" 503.5 503.5 "5000 *W/m^2/K" "1.0246 / GJ" hot "Steam@11.22atm" 457.6 457.6 "5000 *W/m^2/K" "0.773824 / GJ" hot "Steam@4.08atm" 417.0 417.0 "5000 *W/m^2/K" "0.573203 / GJ" hot "Steam@1.70atm" 388.2 388.2 "5000 *W/m^2/K" "0.41796 / GJ" cold "ColdWater@32.2degC" 305.2 305.2 " 500 *W/m^2/K" "0.0668737 / GJ" cold "Ammonia@1degC" 274.00 274.00 " 500 *W/m^2/K" "1.65035 / GJ" cold "Ammonia@-17.68degC" 255.32 255.32 " 500 *W/m^2/K" "2.96871 / GJ" cold "Ammonia@-21.67degC" 251.33 251.33 " 500 *W/m^2/K" "3.96226 / GJ" end # all the data we need for this problem has been defined. Now set the # global data variables that are used by the synthesis procedure # before attempting to define the actual synthesis problem. Data criteria costs # rank according to given criteria hex hex # use heat exchanger model specified utils utils print # output all settings end

With these setting, we can now proceed to define the flowsheet. The first requirement is the definition of the feed stream. This requires us to define the species that appear in the process along with the data required for the estimation of the necessary physical properties, such as bubble and dew points for mixtures.

vle.KistaComponent benzene bp 353.2 cpv -33.917 4.743e-1 -3.017e-4 7.130e-8 lhtc " 500 *W/m^2/K" vhtc "5000 *W/m^2/K" end vle.KistaComponent C6H5Cl bp 405.2 cpv -33.888 5.631e-01 -4.521e-04 1.426e-07 lhtc " 500 *W/m^2/K" vhtc "5000 *W/m^2/K" end vle.KistaComponent p-C6H4Cl2 bp 447.3 cpv -14.344 5.534e-01 -4.559e-04 1.447e-07 lhtc " 500 *W/m^2/K" vhtc "5000 *W/m^2/K" end vle.KistaComponent C6H3Cl3 bp 486.2 cpv -14.361 6.087e-01 -5.622e-04 2.072e-07 lhtc " 500 *W/m^2/K" vhtc "5000 *W/m^2/K" end

With the components known, we can define the stream as a single liquid phase. The phase composition is defined using the quantities defined earlier. It is also useful to define the base discretisation values although these are not strictly necessary for process simulation and optimisation.

vle.Phase feedPhase add benzene Fbenzene add C6H5Cl Fc6h5cl add p-C6H4Cl2 Fpc6h4cl2 add C6H3Cl3 Fc6h3cl3 base benzene "Base * Fbenzene" base C6H5Cl "Base * Fc6h5cl" base p-C6H4Cl2 "Base * Fpc6h4cl2" base C6H3Cl3 "Base * Fc6h3cl3" end vle.Stream feed add feedPhase nstates nPs T 313 P "1 *atm" print end

We are now ready to define the units of the process. We start with the feed tank and the product tanks. The latter include the specification constraints that ensure the process designed meets the requirements.

vle.FeedTank feedtank Stream feed feed end # the tops of the first distillation will go into a tank for pure # benzene hopefully. The benzene is destined to be recycled back to # the reaction section. We want at least 98% purity and no more than # 0.005 kmol/s of Chlorobenzene. vle.ProductTank pureBenzene Expression spec "(x[benzene]>0.98) & (F*x[C6H5Cl] < 0.005)" end # We require a purity of Chlorobenzene of at least 90%. vle.ProductTank pureC6H5Cl Expression spec "x[C6H5Cl]>0.90" end # finally, the product tank that accepts waste streams: Any other # stream which has a composition of less than 10% Benzene and 10% # Chlorobenzene is allowed as a waste stream. vle.ProductTank otherProducts Expression spec "(x[benzene]<0.10) & (x[C6H5Cl]<0.10)" end

The processing units are three distillation columns. Each of them has a number of design variables which define the search space for the optimisation procedure. These include the reflux rate factor. `rrf`

, the tray efficiency and the key recovery.

# the first distillation unit has benzene as the light key vle.Distillation dist-1 Component key benzene Real rrf 1.2 1.05 1.5 10 Real trayefficiency 1.0 1.0 1.0 1 Boolean eduljee false Boolean conceptual false Boolean sfa false Boolean totalcondenser true new tunable+specification "Recovery of light and heavy keys" Real rec 0.95 0.800 0.999 10 LogReal P 5.039684199579494 1.0 32.0 16 end # the second distillation unit has benzene as the light key as well. vle.Distillation dist-2 Component key benzene Real rrf 1.2 1.05 1.5 10 Real trayefficiency 1.0 1.0 1.0 1 Boolean eduljee false Boolean conceptual false Boolean sfa false Boolean totalcondenser true new tunable+specification "Recovery of light and heavy keys" Real rec 0.999 0.800 0.999 10 LogReal P 2.0 1.0 32.0 16 end # The next distillation column separates the actual product # (Chlorobenzene) from the waste products. vle.Distillation dist-3 Component key C6H5Cl Real rrf 1.2 1.05 1.5 1 Real trayefficiency 1.0 1.0 1.0 1 Boolean eduljee false Boolean conceptual false Boolean sfa false Boolean totalcondenser true new tunable+specification "Recovery of light and heavy keys" Real rec 0.95 0.800 0.999 10 LogReal P 1.5874010519681996 1.0 32.0 16 end

The remaining units are used to define the superstructure. We have on option: the first distillation unit performs the same separation as the second one. Do we want to use two units, at lower recoveries for the keys, or a single high recovery unit? To define the superstructure, we introduce a `Chooser`

unit which sends the feed either to the first distillation unit or to the second one. To process the outputs, we define two mixers as well.

# there is a choice immediately which is whether the feed goes to the # first distillation unit or skips it and goes to the second # distillation unit. vle.Chooser chooser Int choice 1 0 1 1 end # associated with the chooser is a mixer which will be used to specify # the feed to the second distillation unit as being from the feed tank # or from the bottoms output of the first distillation unit vle.Mixer choiceMixer Boolean allownull true end vle.Mixer benzeneMixer Boolean allownull true end

Everything has now been defined. We can specify the flowsheet structure. This will consist from a number of lines where each line has a *from* unit and a number of *to* units. Each of the output streams of the *from* unit is sent as an input stream to one of the *to* units.

Flowsheet clben build feedtank chooser chooser dist-1 choiceMixer dist-1 benzeneMixer choiceMixer choiceMixer dist-2 dist-2 benzeneMixer dist-3 benzeneMixer pureBenzene dist-3 pureC6H5Cl otherProducts end continuous evaluationmethod breadthfirst evaluate print end

The full input file is here:

# input file for the optimization problem post-synthesis for the # Chlorobenzene separation problem. The structure is actually a # superstructure which will allow us to choose between a 2 unit # solution and a 3 unit solution. The latter allows for lower # recovery in some distillation units. The aim of this input file is # to provide an objective function for optimization of three sets of # variables for the distillation columns in the process flowsheet: the # operating pressure, the reflux rate factor and the recovery as well # as a binary variable for choosing the first unit in the sequence. # Copyright (c) 2005-2014, Eric S Fraga, All rights reserved. project 2or3 # name will be used for report generation # start by specifying that all objects come from the Jacaranda # package, specifically the process synthesis sub-package and the unit # models. use jacaranda.design.ps use jacaranda.design.units # define some global variables variables # the discretization of component flows const Base 0.001 "Base component flow discretization" # define the flows of the components in the feed const Fbenzene 0.97 "Flow of Benzene in feed stream" const Fc6h5cl 0.01 "Flow of C6H5Cl in feed stream" const Fpc6h4cl2 0.01 "Flow of p-C6H4Cl2 in feed stream" const Fc6h3cl3 0.01 "Flow of C6H3Cl3 in feed stream" # set the cost base to 1993 based on the M&S index set msindex 964.2 # and the number of discrete enthalpy (or state) levels for streams. # it is probably a good idea to have this value match the number of # operating pressures in the distillation columns. const nPs 4 "Number of discrete stream enthalpy levels" end # define the ranking criteria: we want to study the difference in # operating versus capital costs so we define two criteria, each of # which is the sum of the relevant criterion value generated by each # unit in a flowsheet. The term in quotes for each criterion is the # expression to evaluate for each unit. The "sum" means that the value # of the criterion is the sum of expression evaluate for all the units # in the resulting flowsheets. Criteria costs criterion CapitalCost sum "capcost" criterion OperatingCost sum "opercost" end # define the heat exchanger model for heating and cooling. We define # two cost functions, one for the capital cost as a function of the # area of the exchanger, and the other the operating cost as a # function of the duty (Q [kJ]), the cost of the utility used (Cu # [$/kJ]), and the time (seconds in a year equal to h*hpy where hpy is # the "hours per year"). To cater for equal temperature differences at # both ends of the exchanger, we use the Chen approximation. HeatExchanger hex model deltain = abs(T1in-T2out) deltaout = abs(T1out-T2in) eps = abs(deltain/deltaout-1) if eps < 4e-4 lmtd = (1+eps/2-eps^2/12)*deltaout else lmtd = (deltain - deltaout)/log(deltain/deltaout) endif A = Q/U/lmtd capcost = 101964 + 92.9*A^0.65 opercost = Cu*Q*h*hpy end end # define the utilities available for meeting heating and cooling # demands from the units. the parameters for each unit are inlet # temperature, outlet temperature, heat transfer coefficient, and # cost. DiscreteUtilities utils hot "Steam @ 28.23 atm" 503.5 503.5 "5000 *W/m^2/K" "1.0246 / GJ" hot "Steam@11.22atm" 457.6 457.6 "5000 *W/m^2/K" "0.773824 / GJ" hot "Steam@4.08atm" 417.0 417.0 "5000 *W/m^2/K" "0.573203 / GJ" hot "Steam@1.70atm" 388.2 388.2 "5000 *W/m^2/K" "0.41796 / GJ" cold "ColdWater@32.2degC" 305.2 305.2 " 500 *W/m^2/K" "0.0668737 / GJ" cold "Ammonia@1degC" 274.00 274.00 " 500 *W/m^2/K" "1.65035 / GJ" cold "Ammonia@-17.68degC" 255.32 255.32 " 500 *W/m^2/K" "2.96871 / GJ" cold "Ammonia@-21.67degC" 251.33 251.33 " 500 *W/m^2/K" "3.96226 / GJ" end # now define the actual process flowsheet which consists of three # distillation columns and four specific product tanks, as well as a # feed tank! # the first unit is the feed tank for which we also need to define the # actual feed stream. This stream consists of the output of # the reactor and there are 4 different components. All the components # of the stream are defined using the KistaComponent class. This # component class uses physical properties estimation methods based on # the 1 atm boiling point of the component and the coefficients of the # vapour heat capacity polynomial correlation (available from most # good chemical engineering resource books...). The component # definitions include the specification of the smallest amount of that # component that can be represented in the discrete space used by the # Jacaranda search procedure. These minimum amounts are based on the # feed stream amount multiplied by a base discretization value, # defined above. vle.KistaComponent benzene bp 353.2 cpv -33.917 4.743e-1 -3.017e-4 7.130e-8 lhtc " 500 *W/m^2/K" vhtc "5000 *W/m^2/K" end vle.KistaComponent C6H5Cl bp 405.2 cpv -33.888 5.631e-01 -4.521e-04 1.426e-07 lhtc " 500 *W/m^2/K" vhtc "5000 *W/m^2/K" end vle.KistaComponent p-C6H4Cl2 bp 447.3 cpv -14.344 5.534e-01 -4.559e-04 1.447e-07 lhtc " 500 *W/m^2/K" vhtc "5000 *W/m^2/K" end vle.KistaComponent C6H3Cl3 bp 486.2 cpv -14.361 6.087e-01 -5.622e-04 2.072e-07 lhtc " 500 *W/m^2/K" vhtc "5000 *W/m^2/K" end # using these components, define the feed stream as a single phase at specified # temperature and pressure. First define the phase object with the # correct amounts of each component. vle.Phase feedPhase add benzene Fbenzene add C6H5Cl Fc6h5cl add p-C6H4Cl2 Fpc6h4cl2 add C6H3Cl3 Fc6h3cl3 # also define the granularity of the component flows for each # component. This is the basic discretization used by Jacaranda to # ensure that the search graph generated implicitly is finite in # size. The granularity should be appropriate for the particular # problem and, in particular, should be consistent with # discretizations performed in the separation units (for example) and # the product specifications. In this problem, we have specified the # same discretization factor ("Base" as defined in the main input # file) for each component as a function of its flow in the feed # stream. The individual flows of the components in the feed stream # have been defined in the main input file as well. base benzene "Base * Fbenzene" base C6H5Cl "Base * Fc6h5cl" base p-C6H4Cl2 "Base * Fpc6h4cl2" base C6H3Cl3 "Base * Fc6h3cl3" # output information about this phase object which will include the # component discretizations defined above show end # The stream definition includes, as well as the main phase object # defined above, the specification of the initial temperature and # pressure and the number of discrete pressure levels which can be # represented. This number, nPs, is defined in the main input file. vle.Stream feed add feedPhase nstates nPs T 313 P "1 *atm" print end # finally define the actual feed tank which will output the stream # defined immediately above vle.FeedTank feedtank Stream feed feed end # there is a choice immediately which is whether the feed goes to the # first distillation unit or skips it and goes to the second # distillation unit. vle.Chooser chooser Int choice 1 0 1 1 end # associated with the chooser is a mixer which will be used to specify # the feed to the second distillation unit as being from the feed tank # or from the bottoms output of the first distillation unit vle.Mixer choiceMixer Boolean allownull true end vle.Mixer benzeneMixer Boolean allownull true end # the first distillation unit has benzene as the light key jacaranda.design.units.vle.Distillation dist-1 Component key benzene Real rrf 1.2 1.05 1.5 10 Real trayefficiency 1.0 1.0 1.0 1 Boolean eduljee false Boolean conceptual false Boolean sfa false Boolean totalcondenser true new tunable+specification "Recovery of light and heavy keys" Real rec 0.95 0.800 0.999 10 LogReal P 5.039684199579494 1.0 32.0 16 end # the tops of the first distillation will go into a tank for pure # benzene hopefully. The benzene is destined to be recycled back to # the reaction section. We want at least 98% purity and no more than # 0.005 kmol/s of Chlorobenzene. jacaranda.design.units.vle.ProductTank pureBenzene Expression spec "(x[benzene]>0.98) & (F*x[C6H5Cl] < 0.005)" end # the second distillation unit has benzene as the light key as well. jacaranda.design.units.vle.Distillation dist-2 Component key benzene Real rrf 1.2 1.05 1.5 10 Real trayefficiency 1.0 1.0 1.0 1 Boolean eduljee false Boolean conceptual false Boolean sfa false Boolean totalcondenser true new tunable+specification "Recovery of light and heavy keys" Real rec 0.999 0.800 0.999 10 LogReal P 2.0 1.0 32.0 16 end # The next distillation column separates the actual product # (Chlorobenzene) from the waste products. jacaranda.design.units.vle.Distillation dist-3 Component key C6H5Cl Real rrf 1.2 1.05 1.5 1 Real trayefficiency 1.0 1.0 1.0 1 Boolean eduljee false Boolean conceptual false Boolean sfa false Boolean totalcondenser true new tunable+specification "Recovery of light and heavy keys" Real rec 0.95 0.800 0.999 10 LogReal P 1.5874010519681996 1.0 32.0 16 end # We require a purity of Chlorobenzene of at least 90%. jacaranda.design.units.vle.ProductTank pureC6H5Cl Expression spec "x[C6H5Cl]>0.90" end # finally, the product tank that accepts waste streams: Any other # stream which has a composition of less than 10% Benzene and 10% # Chlorobenzene is allowed as a waste stream. jacaranda.design.units.vle.ProductTank otherProducts Expression spec "(x[benzene]<0.10) & (x[C6H5Cl]<0.10)" end # all the data we need for this problem has been defined. Now set the # global data variables that are used by the synthesis procedure # before attempting to define the actual synthesis problem. Data criteria costs # rank according to given criteria hex hex # use heat exchanger model specified utils utils print # output all settings end # all the units have been defined so we can create the actual process # flowsheet. jacaranda.design.ps.Flowsheet clben build feedtank chooser chooser dist-1 choiceMixer dist-1 benzeneMixer choiceMixer choiceMixer dist-2 dist-2 benzeneMixer dist-3 benzeneMixer pureBenzene dist-3 pureC6H5Cl otherProducts end continuous evaluationmethod breadthfirst evaluate print end

## Optimisation with external models

In the previous blog entry, I described how Jacaranda has been extended to allow models to be written in other languages, specifically in GAMS. Octave is also supported. This blog entry shows how these models can be used by the optimisation methods implemented within Jacaranda. An example employing the basic genetic algorithm is presented.

The key to using a model written in an external modelling system is
the definition of an object which instantiates the
`ModelBasedObjectiveFunction`

class. The following example uses the external
gams model which defines a MINLP described by Westerlund & Westerlund
(2003).

use jacaranda.util.opt ModelBasedObjectiveFunction model model gams code westerlund.gms # the base gams model code name westerlund # the GAMS model to use command "/opt/gams/22.8/gams" # how to run gams on this system # next two lines initialise GAMS variables x and y # from Jacaranda's own x (continuous) and y (integer) variables # defined by the Jacaranda model object in x[0] x.fx in y[0] y.fx # the final value of the z GAMS variable will be copied # back into Jacaranda's z variable out z.l z # define the task for GAMS objective "solve westerlund using minlp minimizing z" result "z" # what to return upon evaluation end # lower and upper bounds a 1 1 # lower bound on both x and y variables b 6 6 # and upper bound as well # evaluate/solve the model using x=1.1 and y=1 as initial # values and return the criteria values (z in our case) evaluate 1.1 1 print show end

This objective function instance will make use of an external model
written using the GAMS modelling language and will be solved using
GAMS. This optimisation problem has one real variable (`x[0]`

in
Jacaranda's own modelling language) and one integer variables (`y[0]`

in Jacaranda) which map to `x`

and `y`

in GAMS. In the GAMS model,
these variables are fixed and the model in GAMS is a square
system. The result of the GAMS solution will be in `z`

which is copied
back to the same named variable in Jacaranda.

Once defined as an objective function, it can be used anywhere in Jacaranda where an objective function is allowed. This includes, for instance, the genetic algorithm classes. The evolution of the solutions obtained by this genetic algorithm for this problem is illustrated in this figure:

Figure 2: Statistics for population as it evolves under the control of a genetic algorithm

The commands to generate this plot are created automatically by Jacaranda and placed in the org-mode formatted output file.

## External models in Jacaranda

Every object in the Jacaranda system can have a model associated with it. By default, these models are written in the Jacaranda Expression language. This language supports vector based arithmetic but is essentially a calculator. Sometimes, a more powerful modelling language is required.

Jacaranda has been extended to allow the use of external modelling systems. A framework has been put in place. At the moment, only one external language interface has been defined: for the GAMS modelling system. With this interface, a model can be written in GAMS and Jacaranda can communicate with GAMS to transfer values into GAMS and back out again.

#### A small GAMS modelling example

Consider the following Jacaranda input file:

use jacaranda.base define variable x1upper 2.5 # this will be used to set a GAMS value below end EGO gamsmodel model gams code test.gms # where the base gams model code is command "/opt/gams/22.8/gams" # how to run gams on this system # use x1upper Jacaranda variable to set GAMS variable X1.UP in x1upper X1.UP # and take result from GAMS of z and place in Jacaranda's z value out z.l z objective "solve test using nlp minimizing z" result z # evaluate this expression to generate result end evaluatemodel # do it! print end

This example creates a simple object which defines the model
associated with the object as being an external GAMS model. The GAMS
model is in the file `test.gms`

and includes everything but the actual
`solve`

directive. The `solve`

directive is specified by the
`objective`

statement in Jacaranda.

The test code for this example is here:

$TITLE Test Problem $OFFSYMXREF $OFFSYMLIST * Example from Problem 8.26 in "Engineering Optimization" * by Reklaitis, Ravindran and Ragsdell (1983) * Code from * http://www.che.boun.edu.tr/courses/che477/gms-mod.html#TEST.GMS * (from a course by Ignacio Grossmann) VARIABLES X1, X2, X3, Z ; POSITIVE VARIABLES X1, X2, X3 ; EQUATIONS CON1, CON2, CON3, OBJ ; CON1.. X2 - X3 =G= 0 ; CON2.. X1 - X3 =G= 0 ; CON3.. X1 - X2**2 + X1*X2 - 4 =E= 0 ; OBJ.. Z =E= SQR(X1) + SQR(X2) + SQR(X3) ; * Upper bounds X1.UP = 5 ; X2.UP = 3 ; X3.UP = 3 ; * Initial point X1.L = 4 ; X2.L = 2 ; X3.L = 2 ; MODEL TEST / ALL / ; OPTION LIMROW = 0; OPTION LIMCOL = 0;

The Jacaranda input defines a new value for the upper bound on `X1`

and the result of evaluating the model is the value of the `z`

variable, specifically the *level* of that variable, `z.l`

. The `in`

and `out`

directives specify how to bring values *in* to the model
from Jacaranda and then back *out* again.

The output from Jacaranda, in org-mode in Emacs, can be seen here:

where we can see that the result of solving the GAMS model is 7.25.
This is actually a higher value than the optimum because we have
restricted variable `X1`

to a smaller upper bound, to illustrate the
transfer of values from Jacaranda to GAMS, and the optimum lies above
this upper bound.

The result can be any expression and not just a single variable. The
expression uses Jacaranda's own modelling language but has access to
any variables exported from the GAMS model (`z.l`

in this case). Any
number of variables can be imported into the GAMS model and any number
exported as well.

## The Genetic Algorithm classes

One of the built-in optimisation tools in the Jacaranda system is based on genetic algorithms. This short note describes the layout of the relevant classes and illustrates their use with a simple MINLP problem. The aim is not only to illustrate their use but also to explain how these classes can be extended or used directly to build new targeted genetic algorithm solution methods.

All classes and interfaces described in this section are in the
`jacaranda.util.opt`

package unless stated otherwise.

### The GA classes in Jacaranda

The following figure shows the three base classes which make up the genetic algorithm system. The GA class is the main controlling class. It will have a single population which must be provided during the definition of an instance of the GA object. The population will consist of a set of chromosomes, with the initialisation requiring the provision of a single chromosome to seed the population. The population may also be given diversity and selection objects which are discussed below. The chromosome is an interface; there is at least one base implementation, also described below, but it is through defining a new chromosome implementation that the method can be tailored for specific problems.

Figure 4: Base class hierarchy for the Genetic Algorithm procedure in Jacaranda

An implementation of the Chromosome interface,
`VariableEncodedChromosome`

, is available in Jacaranda, suitable for
general MINLP (mixed integer nonlinear programme) problems. These
problems are defined by a combination of real and integer decision or
optimisation variables, one or more objective functions, possibly
constraint equations, both equality and inequality, and bounds on the
decision variables. This general Chromosome implementation is
actually more general than this and is suitable for any object that
can be encoded into and decoded from a combination of real and integer
variables but whose fitness can be evaluated through an objective
function object.

Figure 5: Variable encoded chromosome uses an objective function object for evaluation

The `mate`

and `mutate`

methods use appropriate operations for MINLP
problems. Specifically, the crossover operation is based on a
multi-point crossover across both real and integer variables. The
mutation operator selects on variable, randomly from the set of both
real and integer variables, and gives it a new value randomly from the
domain for that variable. These operators are actually implemented by
the `Encoding`

implementation. The default encoding used by the
`VariableEncodedChromosome`

is the `GeneralEncoding`

class.
Alternative encodings include the `BoundaryMutationEncoding`

, which
replaces the mutation operation with one which emphasises a move to
the boundaries for the selected variable, and the `BinaryEncoding`

,
which encodes each real and integer value in a prespecified number of
bits for each variable.

The `NLP`

and `MINLP`

classes implement the `ObjectiveFunction`

interface and provide the ability to define the actual objective
functions (Jacaranda support multi-objective optimisation fully),
equality and inequality constraints and the lower and upper bounds on
the decision variables.

### An MINLP example

1 use jacaranda.util.opt 2 3 echo "----------------------------------------------- Objective function" 4 MINLP model 5 # objective function with both real, x, and integer, y, variables 6 f "(x[0]-5)^2+(x[1]-3)^2-y*sqrt($x)" 7 # inequality contraints. 8 g "{ 2-(x[0]+x[1]), (x[0]+x[1])-10 }" 9 # lower and upper bounds 10 a "{0, 0}" "0" 11 b "{10, 10}" "1" 12 end 13 14 echo "----------------------------------------------- Chromosome" 15 VariableEncodedChromosome var 16 function model 17 end 18 19 echo "----------------------------------------------- Selection" 20 RouletteSelection roulette 21 end 22 23 echo "----------------------------------------------- Population" 24 Population pop 25 chromosome var 26 crossover 0.75 27 elite 1 28 mutation 0.02 29 n 20 30 selection roulette 31 end 32 33 echo "----------------------------------------------- GA" 34 GA ga 35 outputlevel 2 36 population pop 37 ngen 50 38 solve 39 print 40 end

## Octave interface to Jacaranda

Octave can be used with Jacaranda for access to more powerful or specific optimisation and data analysis methods. A new interface for Jacaranda from Octave has been defined.

#### Introduction

An interface to Jacaranda from Octave (a tool similar to, and often
very compatible with, Matlab) is potentially very useful, especially
in the context of optimisation. Jacaranda provides a framework for
process definitions, including simple or complex physical properties
and processing units. Although Jacaranda has a number of optimisation
methods included, it is not meant to be all-encompassing. As much of
the optimisation research world-wide uses Octave or Matlab, an
interface from Octave (due to its **open source nature** in preference to
the proprietary and closed source Matlab) can be useful.

Previous versions of Jacaranda have included such an interface. However, the interface was based on the use of socket communication which could be slow when many objective function evaluations were required. It was also not a very portable solution which, although not often an issue (Jacaranda runs well on almost any Linux distribution), there is not reason to be limiting in portability.

Recently, a generic interface from Octave to Java has been implemented by Michael Goffioul. Jacaranda has therefore been updated to make use of this interface as it provides a much more elegant solution than what Jacaranda had before and is also more efficient.

#### An example

The full Jacaranda distribution (provided to collaborators) includes an input file which demonstrates this interface. As the more general distribution does not include this particular input file, I include it here for illustration:

1 ## Test out the Octave Java interface to Jacaranda. 2 ## 3 ## Eric S Fraga 4 function jacaranda_test 5 persistent version; 6 if (isempty(version)) 7 version = "2008.08.05 23:55:43"; 8 printf ("ESF %s, Octave java to Jacaranda function evaluation\n", version) 9 endif 10 ## initialise the Jacaranda interface from Octave, using a test input 11 ## file which defines an appropriate objective function. The 12 ## particular example, from Rathore et al (1974) is the design of a 13 ## heat integrated distillation sequence. The input file will 14 ## generate at least one flowsheet design. This flowsheet will define 15 ## an objective function with 8 real optimisation variables and 3 16 ## criteria for optimisation. 17 ## 18 ## Note that the following assumes that the Jacaranda class files are 19 ## stored in the given location relative to this file and that, 20 ## therefore, Octave has been started in this directory. 21 [A B nx ny nf] = jacaranda_init ("../vle/rathore_hen.in", \ # input file 22 "rathore_hen-1-1", \ # objective function 23 "../../src/java") # Jacaranda classpath 24 ## now evaluate the flowsheet at a number of random points 25 N = 100; 26 Y = []; 27 n_infeasible = 0; 28 for i=1:N 29 x = A + rand(1,length(A)).*(B-A); 30 try 31 y = jacaranda_evaluate (x); 32 Y = [Y; y]; 33 catch 34 n_infeasible++; 35 end_try_catch 36 endfor 37 if n_infeasible > 0 38 printf ("There were %d infeasible design points (out of %d total) found.\n", n_infeasible, N); 39 endif 40 printf ("Minimum criteria values: %8.2e %8.2e %8.2e\n", min(Y)); 41 printf ("Maximum criteria values: %8.2e %8.2e %8.2e\n", max(Y)); 42 ## the results obtained above are plotted to give some indication of 43 ## the variation in the three criteria for random design points 44 plot (Y); 45 endfunction

I believe the comments describe clearly what is happening so I will leave it at this for now.

## Defining simple unit models in Jacaranda

Simple unit models can be written using the `Unit`

class in Jacaranda
with the equations written in the `expr`

expression evaluator
available in Jacaranda.

### Introduction

Although the Jacaranda system comes with a number of pre-defined process unit models (e.g. Distillation), there are many problems in conceptual design that only require simple models. These simple models may not require complex physical property values and may consist of, for instance, idealised mass and energy balances. For these cases, it may be worthwhile to define ad hoc simple models.

Jacaranda provides a generic model for such cases: `units.Unit`

^{1},
intended for use with streams supporting vapour liquid equilibria
properties (although methods for estimating specific VLE properties
won't actually be accessible).

Defining an actual model instance using this generic model consists of a number of steps: specifying the process streams that interact with the unit, definining design variables (to allow for analysis and optimisation), defining the equations that describe the unit operation, and defining the equations that can be used for analysis or evaluation (e.g. values used by the criteria definitions). These are described in detail below through the use of a simple example.

### The example problem

The problem we wish to consider is the production of HF, as described
in Laing & Fraga (*A case study on synthesis in preliminary design*,
Computers & Chemical Engineering **21**:S53-S58, 1997). Potential
candidate designs for the process need to consider a wide range of
units with varying degrees of detail. The units required include a
kiln (essentially a reactor), an acid blender, an absorber, a quench
unit and distillation. The distillation unit is modelled through the
detailed (albeit short-cut) model available in Jacaranda
(`units.vle.Distillation`

) whereas the others can be represented using
simpler models.

#### Process streams

There are three types of streams which interact with process units: input (a.k.a. feed) streams, makeup streams (feed streams whose definition depends on the unit operation) and the outputs of the unit. Although makeup streams are really input streams, they differ from input streams in that they are defined as a result of the unit design. The specific input streams, however, will be specified to the unit before the design is attempted.

The kiln unit has one input stream, the main feed, two output streams,
the **gas** stream which is vapour and is the main product of the unit and
the **waste** stream which is in solid phase, and an **air** make-up stream,
of known composition but with the flow rate to be determined by the
design model:

use jacaranda.design.units Unit kiln input feed output gas output waste makeup air air

The feed stream will be specified by Jacaranda prior to any design being attempted.

Output streams are created by the Unit model itself. The general
syntax of the `output`

command is:

output NAME [TYPE]

where the square brackets indicate that the **type** of the output stream
is optional. If specified, this should be a class which implements
the `ps.StreamExpressionInterface`

. If the type is not given, as in the
example above, the system will create a stream of the default type,
`units.vle.Stream`

The syntax for the makeup command is

makeup NAME INSTANCE

where the **instance** should be an actual stream which describes the composition of the makeup stream to be used. The amount or rate of that stream will be defined by the design model equations. The `air`

makeup stream definition is

Stream air phase components O2 CO2 N2 x 0.20 0.0325 0.7675 vapour end end

All streams, whether input, output or makeup, will have variables
available for use in the design equations. The actual variables
defined will depend on the stream definition. For the default base of
`units.vle.Stream`

, the following variables are defined: **F**, the
amount or flow of the stream; **x**, the molar or mass composition in
fractions; **P**, the pressure in atm.; **T**, the temperature in Kelvin;
and, **state**, the state of the stream (0 is solid, 1 is liquid and 2
is vapour). For input streams, these variables are defined and set
before the design is evaluated; for output streams, the design
equations should give values to all of these variables as they will
then be used to define the actual output streams. For makeup streams,
the composition, **x**, will have been defined but all others will be
undefined and should be set by the design model.

#### Input stream checking

Before attempting a design, Jacaranda will query the unit model to see if the input streams specified are appropriate or not. In other words, the unit will be given a chance to indicate whether the feed streams assigned to the unit are suitable for that unit. The unit does this by specifying a set of conditions for the feed:

feedconditions LOGICAL_EXPRESSION

which, for the kiln example, looks like this:

feedconditions "feed.x[CaF2]>epsilon & feed.x[H2SO4] > epsilon"

This is using the `feed`

input stream defined above and looks to see if
the stream contains any significant amounts of \(CaF_2\) and
\(H_2SO_4\).

#### Heat transfer requests

Most units will require heating or cooling (or both). Requests for
heating or cooling are defined using the **heat transfer request** (HTR)
type in Jacaranda. Such requests are defined using the `htr`

command:

htr NAME

which creates a set of variables for specifying the necessary
information for a heat request, much as stream variables are defined.
The components of a heat transfer request are **Q**, the duty (in kW with
negative values indicating that heating is required and positive
values indicating that the unit has an excess of heat so cooling is
required); **T1**, the inlet temperature for the process stream to be
heated or cooled; **T2**, the outlet temperature of the process stream;
and, **U**, the heat transfer coefficient \(\left(\frac{kW}{m^2
\mbox{K}}\right )\) for the process stream side of any exchange.

For the kiln unit, we define:

htr heat

#### Design Variables

Design variables are variables which alter the behaviour or design of
a unit. These variables are intended for Jacaranda to manipulate,
either through the synthesis procedure or through generic optimisation
methods within Jacaranda. For the synthesis procedure, it is
sufficient to indicate that the variable is of type **specification**; for
the general optimisation use, the variables must also be defined as
**tunable**.

For the kiln unit, we define one such variable:

new specification "Single pass conversion of CaF2" RealSet conversion 0.95 0.96 0.97 0.98 0.99

The general format for defining a new variable is

new FUNCTION DESCRIPTION TYPE NAME VALUES

where FUNCTION is either `specification`

or `specification+tunable`

,
DESCRIPTION is a string which is purely decorative, TYPE is one of
`Real`

, `RealSet`

, `Int`

, `IntSet`

or `Component`

, and VALUES describe the range
of values the variable should consider when used in either synthesis
or optimisation modes. For a `RealSet`

variable, the values are a list
of discrete values. For other types, the values will be specified in
different ways. For instance, for the `Real`

type, the definition would
look like one of the following:

new specification "Continuous design variable" Real x1 VALUE new specification "Continuous design variable" Real x2 MIN MAX N new specification "Continuous design variable" Real x3 VALUE MIN MAX N

In the first case, the variable is given the specified value and the minimum and maximum values this variable can have are also this value. This is not particularly useful for design (either for synthesis or optimisation) but could be useful in debugging. The more common case is the second where the definition says that \(x_2 \in \left [ MIN , MAX \right ]\).

When using the synthesis procedures in Jacaranda, **N** indicates the
number of discrete values for this variable within the interval
specified. These discrete values will be uniformly distributed across
the interval, including both end-points if \(N>1\). If a **log**
based distribution of the discrete points is required, one can use the
`LogReal`

type instead of `Real`

.

The final case is the same as the second except that the variable is initialised to have the specific value given which may or may not correspond to one of the discrete values the system would normally use. This will not affect the synthesis procedures but will be useful for the process optimisation methods in Jacaranda.

#### Design Equations

When Jacaranda wishes to generate a design of the unit, it will
evaluate the equations given using the `designequations`

command. These
equations are evaluated by the `jacaranda.util.expr`

expression
evaluator. This is essentially a vector based calculator and does ** not**
solve a system of simultaneous equations. If solving a complex system
of equations is required, the simple unit model is probably not the
approach one should take for defining the model. Alternative
approaches are beyond the scope of this short note so readers are
encouraged to approach the author for suggestions.

For the kiln model, the following are snippets of the equations we use with some commentary:

designequations # initialize the individual stream flows. we use the feed stream # composition vector to create new vectors of the correct size. gas.f = 0*feed.x waste.f = 0*feed.x feed.f = feed.F*feed.x # feed stream individual component flows

The `feed`

stream will have been defined by Jacaranda and the
appropriate variables (**F**, **x**, **T**, …) defined. These variables are
accessed by the stream name (as given in the `input`

, `output`

and `makeup`

commands above) followed by the full stop (.) and then the actual
variable name. The expression

feed.f = feed.F*feed.x

creates a vector of feed component flows based on the total flow, **F**,
and the composition, **x**, of the feed stream.

# the air intake is directly related to the feed stream flow rate air.F = air.ratio * feed.F air.f = air.F * air.x

These equations define the actual amount of the `air`

makeup stream
required. Recall that a makeup stream will have the composition
already defined.

# determine extents of reactions extent = {0, 0, 0} if feed.f[CaCO3] > feed.f[H2SO4] extent[0] = 0 extent[1] = feed.f[H2SO4] else extent[1] = feed.f[CaCO3] if feed.f[CaF2] > (feed.f[H2SO4]-extent[1]) extent[0] = (feed.f[H2SO4]-extent[1])*conversion else extent[0] = feed.f[CaF2]*conversion endif endif if feed.f[SiO2] > (4*(extent[0]-feed.f[HF])) extent[2] = (feed.f[HF]+extent[0])/4 else extent[2] = feed.f[SiO2] endif

These equations determine the extends of reaction. There are three reactions and it is important to note that the expression evaluator in Jacaranda uses a zero-based indexing for vectors.

Given the extents of reaction, the output streams can now be defined based on a simple extents of reaction mass balance procedure:

# now define the actual output stream compositions gas.f[H2SO4] = feed.f[H2SO4] - extent[0] - extent[1] gas.f[HF] = feed.f[HF] + 2*extent[0] - 4*extent[2] gas.f[H2O] = extent[1] gas.f[CO2] = extent[1] + air.f[CO2] gas.f[SiF4] = extent[2] gas.f[O2] = air.f[O2] gas.f[N2] = air.f[N2] gas.F = $gas.f # total gas flow gas.x = gas.f/gas.F # and actual composition gas.T = feed.T gas.P = feed.P gas.state = vapour waste.f[CaF2] = feed.f[CaF2] - extent[0] waste.f[CaCO3] = feed.f[CaCO3] - extent[1] waste.f[SiO2] = feed.f[SiO2] - extent[2] waste.f[CaSO4] = extent[0] + extent[1] waste.F = $waste.f # total waste stream flow waste.x = waste.f/waste.F # and composition waste.T = feed.T waste.P = feed.P waste.state = solid

Note the use of the `$`

operator which means **sum of all elements of the
vector** to calculate the total flow of the `gas`

and `waste`

streams.

Given the specifications on the output streams, we can now design the actual physical unit. This includes a simple quadrature rule for evaluating an integral numerically:

# finally, design the actual reactor which is based on a numerical # integration, as described above. nsteps = 100 N = 2/3 M = 1/3 k450 = 0.247 E = 38363 k = k450 * exp(-1 * E * (1/feed.T - 1/450)/R) Xf = conversion Xo = preconversion # preconversion Rc = feed.f[CaF2]/(feed.f[CaF2]+feed.f[CaCO3]) # CaF2:CaCO3 ratio Ra = (feed.f[H2SO4]-extent[1])/feed.f[CaF2] # Acid:CaF2 ratio dX = (Xf-Xo)/nsteps rio = (1.0 - (1.0-Xo)^M) / ( k*(Rc*Rc*(Ra-Xo)*(Ra-Xo)*(1.0-Xo)^N) ) X = Xo + dX t = 0 # residence time in minutes while X <= Xf ri = (1.0 - (1.0-X)^M) / ( k*(Rc*Rc*(Ra-X)*(Ra-X)*(1.0-X)^N) ) t = t + dX*(ri+rio)/2 rio = ri X = X + dX endwhile volume = feed.F*t*60 # remember to convert to seconds diameter = (volume*4/(Pi*htod))^(1/3) height = diameter*htod

Finally, given the extents of reaction, we can solve the energy
balance equations to tell us the amount of heating or cooling
required. The numbers calculated are used to define a **heat transfer
request** (HTR) which was defined above:

# now calculate the heat balance; recall that Q for a heat # transfer request will have a positive value for cooling and a # negative value if heating is required so we need to negate the value # we get from the heats of reaction directly. # values in the next equation are heats of reaction [kJ/kmol] heat.Q = - extent*{53.7, 91.1, -189.4} heat.T1 = feed.T heat.T2 = feed.T heat.P = feed.P heat.U = 1 end # of design equations

#### Evaluation equations

Once the design has been generated, Jacaranda will evaluate the **model**
equations which will typically be used to give values to the variables
used to evaluate the criteria for comparing alternative process
designs and for optimisation. These variables will be problem
dependent. For the kiln model, the only variable we need is the
capital cost of the unit:

model capcost = 1917 * diameter^1.066 end end # of Unit kiln definition

### The full example model definition

The complete model definition, comprising all the pieces described above, can be found here:

# -*- text -*- # # Unit model definition for the HF case study. # Eric S Fraga # $Id: kiln.in 2 2004-04-17 20:20:23Z ucecesf $ use jacaranda.design.units # the air makeup stream used by the unit below Stream air phase components O2 CO2 N2 x 0.20 0.0325 0.7675 vapour end end # The kiln is essentially a reactor. There are three reactions: # # CaF2 + H2SO4 -> 2 HF + CaSO4 Hr = 53.7 # CaCO3 + H2SO4 -> CO2 + H20 Hr = -91.1 # SiO2 + 4 HF -> SiF4 + 2 H2O Hr = -189.4 # # The model is again essentially a simple mass balance. There are two # product streams (HF off-gas and solid waste), one feed stream and # one make up stream (air intake) # # The size of the reactor is estimated by first estimating the # residence time based on the following model (from Laing & Fraga, 1997): # # ----------------------------------------------------------------- # Reactor Residence Time # ---------------------- # From internal report (see jwp) # dX/dt = r = k (Rc^2*(Ra-X)^2*(1-X)^N / (1 - (1-X)^M) # # X = conversion of CaF2, # Rc = mol of CaF2 per mol of Ca in fluorspar feed. # Ra = acid:spar ratio # k = rate constant # N & M are constants dependent on rate model # # The rate constant is given by # k(T) = k(450) exp(-E*(1/T -1/450)/R) # # R = 8.3143 kJ/kmolK # Rate models : N : M : k(450) (min^-1X^-2) : E (kj/kmol) # 9 2/3 1/3 0.247 38,263 # 10 1/3 1/3 0.166 35,379 # # Use simpsons rule to integrate dX/r from prereactor conversion(Xo) # to final conversion (X). # # Use rate model 9, for the sake of argument! # # Note: residence time is in minutes! # # ---------------------------------------------------------------- vle.Separator kiln # make sure we can control the number of occurences of this unit limit new specification "Single pass conversion of CaF2" RealSet conversion 0.95 0.96 0.97 0.98 0.99 new input "Reactor height/length to diameter ratio" Real htod 6.5 new input "Pre-reactor conversion" Real preconversion 0.3 new input "Ratio of air to feed" Real air.ratio 0.05 output gas vapour output waste solid makeup air air htr heat # # must ensure that we have some CaF2 in the feed stream # feedconditions "feed.x[CaF2]>epsilon & feed.x[H2SO4] > epsilon" # now the design equations which include defining the extents of the # reactions and the actual sizing information for the unit design # initialize the individual stream flows. we use the feed stream # composition vector to create new vectors of the correct size. gas.f = 0*feed.x waste.f = 0*feed.x feed.f = feed.F*feed.x # feed stream individual component flows # the air intake is directly related to the feed stream flow rate air.F = air.ratio * feed.F air.f = air.F * air.x # determine extents of reactions extent = {0, 0, 0} if feed.f[CaCO3] > feed.f[H2SO4] extent[0] = 0 extent[1] = feed.f[H2SO4] else extent[1] = feed.f[CaCO3] if feed.f[CaF2] > (feed.f[H2SO4]-extent[1]) extent[0] = (feed.f[H2SO4]-extent[1])*conversion else extent[0] = feed.f[CaF2]*conversion endif endif if feed.f[SiO2] > (4*(extent[0]-feed.f[HF])) extent[2] = (feed.f[HF]+extent[0])/4 else extent[2] = feed.f[SiO2] endif # now define the actual output stream compositions gas.f[H2SO4] = feed.f[H2SO4] - extent[0] - extent[1] gas.f[HF] = feed.f[HF] + 2*extent[0] - 4*extent[2] gas.f[H2O] = extent[1] gas.f[CO2] = extent[1] + air.f[CO2] gas.f[SiF4] = extent[2] gas.f[O2] = air.f[O2] gas.f[N2] = air.f[N2] gas.F = $gas.f # total gas flow gas.x = gas.f/gas.F # and actual composition gas.T = feed.T gas.P = feed.P waste.f[CaF2] = feed.f[CaF2] - extent[0] waste.f[CaCO3] = feed.f[CaCO3] - extent[1] waste.f[SiO2] = feed.f[SiO2] - extent[2] waste.f[CaSO4] = extent[0] + extent[1] waste.F = $waste.f # total waste stream flow waste.x = waste.f/waste.F # and composition waste.T = feed.T waste.P = feed.P # finally, design the actual reactor which is based on a numerical # integration, as described above. nsteps = 100 N = 2/3 M = 1/3 k450 = 0.247 E = 38363 k = k450 * exp(-1 * E * (1/feed.T - 1/450)/R) Xf = conversion Xo = preconversion # preconversion Rc = feed.f[CaF2]/(feed.f[CaF2]+feed.f[CaCO3]) # CaF2:CaCO3 ratio Ra = (feed.f[H2SO4]-extent[1])/feed.f[CaF2] # Acid:CaF2 ratio dX = (Xf-Xo)/nsteps rio = (1.0 - (1.0-Xo)^M) / ( k*(Rc*Rc*(Ra-Xo)*(Ra-Xo)*(1.0-Xo)^N) ) X = Xo + dX t = 0 # residence time in minutes while X <= Xf ri = (1.0 - (1.0-X)^M) / ( k*(Rc*Rc*(Ra-X)*(Ra-X)*(1.0-X)^N) ) t = t + dX*(ri+rio)/2 rio = ri X = X + dX endwhile volume = feed.F*t*60 # remember to convert to seconds diameter = (volume*4/(Pi*htod))^(1/3) height = diameter*htod # now calculate the heat balance; recall that Q for a heat # transfer request will have a positive value for cooling and a # negative value if heating is required so we need to negate the value # we get from the heats of reaction directly. heat.Q = - extent*{53.7, 91.1, -189.4} # values are heats of reaction [kJ/kmol] heat.T1 = feed.T heat.T2 = feed.T heat.P = feed.P heat.U = 1 end # of design equations # cost model model capcost = 1917 * diameter^1.066 end print end # add this unit to the list of units available for process flowsheet # generation edit globalData unit kiln end

This model is copyright Eric S Fraga, all rights reserved. Distribution and use not allowed without his permission.

## Footnotes:

^{1}

In this blog, class names are typically abbreviated by removing
the leading `jacaranda.design.`

prefix. Unless otherwise
noted, all classes should have this prefix to be fully qualified.