A CCP Fuel Blending Example − The Model

As production manager at a fuel processing plant, you would like to maximize profit from blending the raw materials butane, catalytic reformate, and naphtha into the two finished products Regular and Premium. These final products must satisfy certain ranges of quality requirements on octane, vapor pressure, and volatility.  You also know the minimum required for each finished good, the maximum you will be able to sell, and the profit contribution per unit.

Each raw material has a limited availability and cost per unit which are known, however, the quality level of the three raw materials is not known with certainty.  We do know that the quality levels are normally distributed with known means and standard deviations.  We'd like to chose blending ratios so that the finished products don't fall below their lower limits on requirements at least 90 percent of the time and don't exceed their upper limits at least 70 percent of the time.

We will use a CCP to solve this problem, with the quality constraints forming the chance-constrained sets.  The model we will use is available in the LINGO Samples folder under the name BLENDCCP.LG4.

Our model's sets section is:

SETS:

  !Each raw material has an availability

    and cost/unit;

  RAWMAT: AVAIL, COST;

 

  !Each finished good has a min required,

    max sellable, selling price,

    and batch size to be determined;

  FINGOOD: MINREQ, MAXSELL, PRICE, BATCH;

 

  !The quality measures set;

  QUALMES;

 

  !For each combo of raw material and

    quality measure there is an average

    quality level, its standard

    deviation, and the actual quality

    level received;

  RXQ( RAWMAT, QUALMES): QLEVMU, QLEVSD, QLEV;

 

  !For each combination of quality

    measure and finished good there are

    upper and lower limits on quality;

  QXF( QUALMES, FINGOOD): QUP, QLOW;

 

  !For each combination of raw material

    and finished good there is an amount

    of raw material used to be solved for;

  RXF( RAWMAT, FINGOOD): USED;

ENDSETS

A feature to note in the sets section is the attribute QLEV, which will store the actual quality levels of the raw materials.  The actual quality levels are not known with certainty, but they are normally distributed with means QLEVMU and standard deviations QLEVSD.  Also, at the bottom of the sets section, we've also declared the attribute USED, which constitutes our decision variables and will tell us the amount of each raw material to use in each finished product

Next is the model's data section:

DATA:

!Raw materials;

 RAWMAT = BUTANE, CATREF, NAPTHA;

 

!Raw material availability;

 AVAIL =    1000,   4000,   5000;

 

!Raw material costs;

 COST =      7.3,   18.2,   12.5;

 

!Finished goods;

 FINGOOD = REGU, PREM;

 

!Limits on finished goods;

 MINREQ  =    4000,   2000;

 MAXSELL =    8000,   6000;

 

!Finished goods prices;

 PRICE =      18.4,     22;

 

!Quality measures (octane, vapor, volatility);

 QUALMES =    OCT,  VAP,  VOL;

 

!Average quality parameters of

 raw materials;

 QLEVMU =     120,   60,  105,

              100,  2.6,    3,

               74,  4.1,   12;

 

 QLEVSD =       5,    3,    6,

                2,  .11,  .13,

                3,   .2,   .4;

 

!Upper and lower limits on

 quality for each finished good;

 QUP = 100, 120,

        11,  11,

        25,  25;

 QLOW = 90,  95,

         8,   8,

        17,  17;

ENDDATA

At the bottom of the data section, we initialize the averages and standard deviations (QLEVMU and QLEVSD) for the raw material quality levels.  As an example, the average vapor level for catalytic reformate is 60 with a standard deviation of 3.  We also input the upper and lower quality limits for each quality and each finished product (QUP and QLOW).  As an example here, the lower and upper limits on octane for Premium are 95 and 120.  As mentioned, the quality constraints on the finished products will be chance constrained, so we will be able to violate these limits at times.

Next comes the core model:

 !Core model ++++++++++++++++++++++++++++++++++++;

 

 !We want to maximize the profit contribution;

 [R_OBJ] MAX =

  @SUM( FINGOOD( F): PRICE( F) * BATCH( F)) -

   @SUM( RAWMAT( R): COST( R) *

    @SUM( FINGOOD( F): USED( R, F)));

 

 !Subject to raw material availability;

 @FOR( RAWMAT( R):

  [R_RMLIM] @SUM( FINGOOD( F): USED( R, F))

   <= AVAIL( R);

 );

 

 @FOR( FINGOOD( F):

  !Batch size limits;

  @BND( MINREQ( F), BATCH( F), MAXSELL( F));

 

  !Batch size computation;

  [R_BATCOMP] BATCH( F) =

   @SUM( RAWMAT( R): USED( R, F));

 );

 

 @FOR( QXF( Q, F):

  !Quality restrictions for each

   quality measure;

  [R_QUP] @SUM( RAWMAT( R):

   QLEV( R, Q) * USED( R, F)) <=

    QUP( Q, F) * BATCH( F);

  [R_QDN] @SUM( RAWMAT( R):

   QLEV( R, Q) * USED( R, F)) >=

    QLOW( Q, F) * BATCH( F);

 );

Once again, the core model contains the deterministic version of the model without the introduction of any stochastic elements.  Basically, we want to maximize profit, while not using more raw materials than are available, not exceeding batch size limits and strictly meeting the quality restrictions on the finished goods.  As we transition the model to a CCP, we will relax this final condition on the finished goods' quality levels.

Finally, at the end of our model are the declarations that convert the core model into a CCP:

!CCP declarations ++++++++++++++++++++++++++++++;

 

 DATA:

  PROB_UP = .7;

  PROB_DN = .9;

  NSAMP   = 20;

 ENDDATA

 

 !Declare QLEV(Q,F) as normally distributed;

 @FOR( RXQ( R, Q):

  @SPSTGRNDV( 1, QLEV( R, Q));

  @SPDISTNORM( QLEVMU( R, Q), QLEVSD( R, Q),

   QLEV( R, Q));

 );

 

 !Establish two empty CCP row sets with

  a minimal satisfaction probability level;

 @SPCHANCE( 'CCP_QUAL_UP', '>=', PROB_UP);

 @SPCHANCE( 'CCP_QUAL_DN', '>=', PROB_DN);

 

 !Add all the quality constraints to the CCP set;

 @FOR( RXF( R, F):

  @SPCHANCE( 'CCP_QUAL_UP', R_QUP( R, F));

  @SPCHANCE( 'CCP_QUAL_DN', R_QDN( R, F));

 );

 

 !Set sample size;

 @SPSAMPSIZE( 1, NSAMP);

In the following section:

 !Declare QLEV(Q,F) as normally distributed;

 @FOR( RXQ( R, Q):

  @SPSTGRNDV( 1, QLEV( R, Q));

  @SPDISTNORM( QLEVMU( R, Q), QLEVSD( R, Q),

   QLEV( R, Q));

 );

we declare the raw material quality levels as random variables in stage 1 via the @SPSTGRNDV function.  We then declare them as being normally distributed using @SPDISTNORM, along with passing their means and standard deviations.

Next, we need to declare two CCP sets: one for the upper limits on quality, and one for the lower limits on quality.  As mentioned above, we use the @SPCHANCE function to do this as follows:

 !Establish two empty CCP row sets with

  a minimal satisfaction probability level;

 @SPCHANCE( 'CCP_QUAL_UP', '>=', PROB_UP);

 @SPCHANCE( 'CCP_QUAL_DN', '>=', PROB_DN);

Here, we assign the names CCP_QUAL_UP and CCP_QUAL_DN for, respectively, the upper and lower limit constraint sets.  The direction for both sets is greater-than-or-equal-to ('>='), given that we want the probability that the sets are satisfied to equal, or exceed, a specified probability. The desired probability hurdles, PROB_UP and PROB_DN, were initialized in an earlier data section to be .7 and .9.

Once we've declared our CCP sets we need to assign constraints to them.  We do this again with @SPCHANCE:

 !Add all the quality constraints to the CCP set;

 @FOR( RXF( R, F):

  @SPCHANCE( 'CCP_QUAL_UP', R_QUP( R, F));

  @SPCHANCE( 'CCP_QUAL_DN', R_QDN( R, F));

 );

Here, we loop over every (raw material, finished good) pair, loading constraint references (i.e., constraint names) into their respective sets.  You will note that the constraint names were assigned to the quality constraints earlier in the core model:

 @FOR( QXF( Q, F):

  !Quality restrictions for each

   quality measure;

  [R_QUP] @SUM( RAWMAT( R):

   QLEV( R, Q) * USED( R, F)) <=

    QUP( Q, F) * BATCH( F);

  [R_QDN] @SUM( RAWMAT( R):

   QLEV( R, Q) * USED( R, F)) >=

    QLOW( Q, F) * BATCH( F);

 );

You may refer to section Constraint Names for more information on how to establish constraint names.

Lastly, we set the sample size for the model to be 20 with the statement:

 !Set sample size;

 @SPSAMPSIZE( 1, NSAMP);

This is the number of times each random variable will be sampled, and, in the case of CCPs will also be the total number of scenarios.  Note that currently for CCPs there is only one stage, so the sample size is always set for stage 1.  Note that NSAMP was initialized earlier in the model to be 20.

In the next section, we will discuss the solution to our fuel blending example.