Tariff Rates (Power Generation) (mpex15)

/***************************************************************/
/*                                                             */
/*          S A S   S A M P L E   L I B R A R Y                */
/*                                                             */
/*    NAME: mpex15                                             */
/*   TITLE: Tariff Rates (Power Generation) (mpex15)           */
/* PRODUCT: OR                                                 */
/*  SYSTEM: ALL                                                */
/*   PROCS: OPTMODEL                                           */
/*    DATA:                                                    */
/*                                                             */
/* SUPPORT:                             UPDATE:                */
/*     REF:                                                    */
/*    MISC: Example 15 from the Mathematical Programming       */
/*          Examples book.                                     */
/*                                                             */
/***************************************************************/

data period_data;
   input length demand;
   datalines;
6 15000
3 30000
6 25000
3 40000
6 27000
;

data type_data;
   input num_avail min_level max_level unit_cost excess_cost startup_cost;
   datalines;
12  850 2000 1000 2    2000
10 1250 1750 2600 1.30 1000
 5 1500 4000 3000 3     500
;

%let reserve = 0.15;

proc optmodel;
   set PERIODS;
   num length {PERIODS};
   num demand {PERIODS};
   read data period_data into PERIODS=[_N_] length demand;

   set TYPES;
   num num_avail {TYPES};
   num min_level {TYPES};
   num max_level {TYPES};
   num unit_cost {TYPES};
   num excess_cost {TYPES};
   num startup_cost {TYPES};
   read data type_data into TYPES=[_N_]
      num_avail min_level max_level unit_cost excess_cost startup_cost;

   var NumWorking {PERIODS, type in TYPES} >= 0 <= num_avail[type] integer;
   var Excess {PERIODS, TYPES} >= 0;
   var NumStartup {PERIODS, type in TYPES} >= 0 <= num_avail[type] integer;

   impvar Output {period in PERIODS, type in TYPES} =
      min_level[type] * NumWorking[period,type] + Excess[period,type];

   min TotalCost =
      sum {period in PERIODS, type in TYPES} (
         unit_cost[type] * length[period] * NumWorking[period,type]
         + excess_cost[type] * length[period] * Excess[period,type]
         + startup_cost[type] * NumStartup[period,type]);

   con Demand_con {period in PERIODS}:
      sum {type in TYPES} Output[period,type]
   >= demand[period];

   con Reserve_con {period in PERIODS}:
      sum {type in TYPES} max_level[type] * NumWorking[period,type]
   >= (1 + &reserve) * demand[period];

   con Excess_ub {period in PERIODS, type in TYPES}:
      Excess[period,type]
   <= (max_level[type] - min_level[type]) * NumWorking[period,type];

   con Startup_con {period in PERIODS, type in TYPES}:
      NumStartup[period,type]
   >= NumWorking[period,type]
    - (if period - 1 in PERIODS then NumWorking[period-1,type]
       else NumWorking[card(PERIODS),type]);

   solve;
   print NumWorking NumStartup Excess Output;
   create data sol_data from [period type] NumWorking NumStartup Excess Output;

   fix NumWorking;
   fix NumStartup;
   solve with LP relaxint;
   print NumWorking NumStartup Excess Output;
   print {period in PERIODS} (demand_con[period].dual / length[period]);

   unfix NumWorking;
   unfix NumStartup;
   solve with LP relaxint;
   print NumWorking NumStartup Excess Output;
   print {period in PERIODS} (demand_con[period].dual / length[period]);
   print {period in PERIODS} (reserve_con[period].dual / length[period]);
quit;