$title Platoform Example Refinery (FAWLEY,SEQ=65) $onText This simple example refinery is taken from EXXONs Platoform monograph. Three major units, a pipestill, a powerformer and a catalytic cracker are present. The refinery has a choice of three crude oils and sells four final products. The blending is done by recipe and quality specifications. All product flows are measured by weight. The original Platoform version does not give values for the research octane and viscosity blending formulas because of its proprietary nature. A simple functional form has been estimated from the Platoform results for illustrative purposes and the lower specification of "ron" for motor-gas had to be lowered from 99.3 to 99.0 in order to maintain feasibility. Palmer, K H, A Model Management Framework for Mathematical Programming, EXXON Monograph Series. John Wiley and Sons, 1984. Keywords: linear programming, refinery operating, blending problem $offText Set c 'commodities' / arabian-l 'crude' arabian-h 'crude' brega 'crude' lv-naphtha 'light virgin naphtha' v-heat-oil 'virgin heating oil' iv-naphtha 'intermediate virgin naphtha' vacuum-dst 'vacuum distillate' vacuum-res 'vacuum residue' res-arab-l 'vacuum residue - arabian light' res-arab-h 'vacuum residue - arabian heavy' res-brega 'vacuum residue - brega' butane reformate cc-naph-l 'catalytic-cracked naphtha - low severity' cc-naph-h 'catalytic-cracked naphtha - high severity' cc-dist 'catalytic-cracked distillate' fuel-imp 'imported fuel' fuel-equiv 'fuel equivalent' / cr(c) 'crude oils' / arabian-l, arabian-h, brega / ci(c) 'components imported' / fuel-imp / cf 'final products' / motor-gas 'motor gasoline' jet-fuel 'jet fuel' heat-oil 'heating oil' fuel-oil 'fuel oil' / cfq(cf) 'final products quality blended' cfr(cf) 'final products recipe blended' k 'productive units' / pipestill, reformer, c-cracker / p 'processes' / d-arab-l 'distilling arabian light crude' d-arab-h 'distilling arabian heavy crude' d-brega 'distilling brega crude' reform 'reforming' ho-low-s 'cc of heating oil - low severity' ho-high-s 'cc of heating oil - high severity' vd-low-s 'cc of vacuum distillate - low severity' vd-high-s 'cc of vacuum distillate - high severity' / kuse(k,p) / pipestill.(d-arab-l,d-arab-h,d-brega) reformer .(reform) c-cracker.(ho-low-s,ho-high-s,vd-low-s,vd-high-s) / bposs(cf,c) 'blending possibilities for quality blends' / motor-gas.(lv-naphtha,butane,reformate,cc-naph-l,cc-naph-h) fuel-oil .(v-heat-oil,vacuum-dst,res-arab-l,res-arab-h res-brega,cc-dist,fuel-equiv,fuel-imp) / s 'product specification' / rvp 'reid vapor pressure' ron 'research octane number' pct-212f 'percent distilled at 21 degrees f' sulfur 'sulfur content' vbn 'viscosity blending number' / m 'product measure' / volume, weight / ms(m,s) 'measure of specs' / volume.(rvp, ron, pct-212f), weight.(sulfur, vbn) / cfm(cf,m) 'required measure' l 'quality limits' / lower, upper / r 'recipes' / recipe-1*recipe-3 / tr 'transfer options' / tr-1, tr-2 /; Table crdat(cr,*) 'crude oil information table' supply price transport gravity * (1000 tons) ($/bbl) ($/ton) (tons/m3) arabian-l 110 35 24.15 .858 arabian-h 165 34 24.15 .886 brega 80 42 10.05 .823; Table ddat(cf,*) 'demand data' demand price * (1000 tons) ($/ton) motor-gas 40 430 jet-fuel 20 300 heat-oil 50 315 fuel-oil 145 250; Table kdat(k,*) 'capacity data' capacity oper-cost oper-days * (1000bbl/d) ($/bbl f) (days/period) pipestill 65.0 .15 31 reformer 7.5 .60 28 c-cracker 8.0 .65 28; Table ap(c,p) 'process yields (proportion weight of crude feed)' d-arab-l d-arab-h d-brega arabian-l -1.0 arabian-h -1.0 brega -1.0 lv-naphtha .035 .030 .045 iv-naphtha .100 .075 .135 v-heat-oil .390 .300 .430 vacuum-dst .285 .230 .280 res-arab-l .165 res-arab-h .335 res-brega .100 + reform ho-low-s ho-high-s vd-low-s vd-high-s iv-naphtha -1. v-heat-oil -1. -1. vacuum-dst -1. -1. butane .02 .02 .0325 .05 .06 reformate .90 cc-naph-l .275 .325 cc-naph-h .3775 .45 cc-dist .68 .555 .585 .44 fuel-equiv .08 .025 .035 .040 .050; Table recipes(cf,c,r) 'blending recipes (proportions of weight)' recipe-1 recipe-2 recipe-3 heat-oil.v-heat-oil 1.0 heat-oil.cc-dist 1.0 jet-fuel.lv-naphtha .2 jet-fuel.iv-naphtha .3 .1 jet-fuel.v-heat-oil .7 .8 .1 jet-fuel.cc-dist .8; Table at(c,tr) 'transfer processes (proportion of weight)' tr-1 tr-2 lv-naphtha -1 butane -1 fuel-equiv 1.11 1.07; Table specs(cf,l,s) 'product specifications for quality blend' rvp ron pct-212f sulfur vbn motor-gas.lower 10 99 motor-gas.upper 60 fuel-oil.upper 3 37.5; Table prop(c,*) 'properties' rvp ron pct-212f sulfur vbn gravity butane 75 101.6 100 .570 lv-naphtha 12 86.3 95 .650 iv-naphtha .737 reformate 6 102.5 35 .865 cc-naph-l 7 94.9 60 .730 cc-naph-h 9 99.1 64 .750 v-heat-oil 1.0 14.8 .886 vacuum-dst 1.7 21.8 .920 res-arab-l 4.0 48 res-arab-h 5.0 51 res-brega .6 44 cc-dist 1.5 18.0 fuel-imp 3.0 37.5 fuel-equiv 3.5 44 ; Scalar m3tob 'conversion of barrels to m3 (barrels per m3)' / 6.29 / pbmg 'lead content in motor gas (grams per liter)' / .4 / ppb 'lead price ($ per kg)' / 7.8 / ocpb 'lead cost ($ per m3 of gasoline)'; Parameter char(c,m) 'conversion for product balance' bp(k,p) 'capacity utilization (m3 per ton)' kp(k) 'capacity (1000 m3)' oc(k) 'operating costs ($ per m3)' pcr(cr) 'plantgate crude price ($ per ton)' pimp(c) 'price of imported components ($ per ton)' / fuel-imp 245 / invent(c) 'inventory change (1000 tons - buildup positive)' / cc-naph-l -.58 reformate 1.65 / dir(l) 'sign of over-under specs' / lower -1, upper 1 /; cfr(cf) = sum((c,r)$recipes(cf,c,r), yes); cfq(cf) = not cfr(cf); cfm(cf,m) = sum((l,s)$specs(cf,l,s), ms(m,s)); cfm(cf,"weight") = yes; char(c,"weight") = 1; char(c,"volume")$prop(c,"gravity") = 1/prop(c,"gravity"); prop(cr,"gravity") = crdat(cr,"gravity"); bp(k,p)$kuse(k,p) = 1/sum(c$(ap(c,p) < 0), -ap(c,p)*prop(c,"gravity")); kp(k) = kdat(k,"capacity")*kdat(k,"oper-days")/m3tob; ocpb = pbmg*ppb; oc(k) = kdat(k,"oper-cost")*m3tob; pcr(cr) = crdat(cr,"price")*m3tob/crdat(cr,"gravity"); display cfr, cfq, cfm, char, bp, kp, oc, ocpb, pcr; Variable u(c) 'crude purchase (1000 tons)' z(p) 'production levels (1000 tons)' cap(k) 'capacity use (1000 m3)' trans(tr) 'transfer activities (1000 tons)' import(c) 'import of components (1000 tons)' bq(c,cf) 'quality blending activity (1000 tons)' rb(cf,r) 'recipe blending activity (1000 tons)' q(cf,m) 'final product measure (1000 tons)' ov(cf,l,s) 'over or under blending (1000 units)' sales(cf) 'final product sales (1000 tons)' revenue 'final product revenue (1000 $)' recurrent 'operating cost (1000 $)' purchase 'external product purchase (1000 $)' transport 'transport cost (1000 $)' profit 'operating profit (1000 $)'; Positive Variable u, cap, z, bq, rb, q, sales, ov, trans, import; Equation mbal(c) 'component balance (1000 tons)' kbal(k) 'capacity constraints (1000 m3)' dbal(cf) 'demand balance (1000 tons)' qsb(cf,l,s) 'quality constraint (1000 units)' pbal(cf,m) 'product balance (1000 units)' drev 'revenue definition (1000 $)' doper 'recurrent cost definition (1000 $)' dpur 'purchase cost definition (1000 $)' dtran 'transport cost definition (1000 $)' dprof 'profit definition (1000 $)'; mbal(c).. u(c)$cr(c) + sum(p, ap(c,p)*z(p)) + sum(tr, at(c,tr)*trans(tr)) + import(c)$ci(c) =e= sum(cfq$bposs(cfq,c), bq(c,cfq)) + invent(c) + sum((cfr,r), recipes(cfr,c,r)*rb(cfr,r)); kbal(k).. cap(k) =e= sum(p, bp(k,p)*z(p)); dbal(cf).. sales(cf) =e= q(cf,"weight")$cfq(cf) + sum((c,r), recipes(cf,c,r)*rb(cf,r))$cfr(cf); qsb(cfq,l,s)$specs(cfq,l,s).. sum(c$bposs(cfq,c), prop(c,s)*sum(m$ms(m,s), char(c,m)*bq(c,cfq))) + dir(l)*ov(cfq,l,s) =e= sum(m$ms(m,s), specs(cfq,l,s)*q(cfq,m)); pbal(cfq,m)$cfm(cfq,m).. q(cfq,m) =e= sum(c$bposs(cfq,c), char(c,m)*bq(c,cfq)); drev.. revenue =e= sum(cf, ddat(cf,"price")*sales(cf)); doper.. recurrent =e= sum(k, oc(k)*cap(k)) + ocpb*q("motor-gas","volume"); dpur.. purchase =e= sum(cr, pcr(cr)*u(cr)) + sum(ci, pimp(ci)*import(ci)); dtran.. transport =e= sum(cr, crdat(cr,"transport")*u(cr)); dprof.. profit =e= revenue - recurrent - purchase - transport; Model exxon / all /; u.up(cr) = crdat(cr,"supply"); sales.fx(cf) = ddat(cf,"demand"); cap.up(k) = kp(k); solve exxon maximizing profit using lp; Parameter fblend 'summary of blending operation for fuel oil'; fblend(c,"weight") = bq.l(c,"fuel-oil"); fblend("**total**","weight") = q.l("fuel-oil","weight"); fblend(c,"percent") = fblend(c,"weight")/fblend("**total**","weight")*100; fblend("**total**","percent") = 100; fblend(c,"sulfur")$bposs("fuel-oil",c) = prop(c,"sulfur") ; fblend("**total**","sulfur") = sum(c, fblend(c,"percent")*fblend(c,"sulfur"))/100; fblend(c,"vbn")$bposs("fuel-oil",c) = prop(c,"vbn"); fblend("**total**","vbn") = sum(c, fblend(c,"percent")*fblend(c,"vbn"))/100; display fblend;