# GAMS Tutorial 1

**Overview**

The GAMS (General Algebraic Modeling System) package is essentially a compiler for a specialized language devoted to modeling for optimization, linked with a variety of solvers. Executing “gams” in one step parses, compiles, and solves a system of equations derived from the model expressed in the GAMS input file. To illustrate how to model with GAMS, we will optimize a simple model of power generation from two different fuels. The goal is to minimize the cost for one of the fuels while satisfying the operational constraints of power generation. The system will be modeled with both a linear and quadratic constraints. The quadratic problem was described in reference [1].

**LP Problem Formulation**

We have two generators, each of which can run on a mixture of blast furnace gas (BFG; a by-product of iron smelting with fairly low heating value) and fuel oil (FO; heavy petroleum distillate). These generators will be called Generator 1 and Generator 2. Generator 1 can output between 18 and 30 MW, and Generator 2 between 14 and 25 MW. Both generators are linked to a blast furnace, so the rate of BFG consumption is limited to what is produced by the furnace during its smelting operations—this is fixed at 10 units/h, which we can purchase cheaply. Based on the consumers’ requirements for energy, we also need to ensure that the generators can produce a minimum of 50 MW output. Given that the BFG supply is fixed, the difference must be made up from FO combustion. We want to purchase the minimum amount of FO, in order to maximize any profits made from the sale of power (at a fixed price, for this example).

To begin, we assume that the fuel consumption of each generator for a particular fuel is linear with respect to the output power, with known constant coefficients:

Generator 1, FO |
0.18223 |

Generator 1, BFG |
0.19572 |

Generator 2, FO |
0.24372 |

Generator 2, BFG |
0.27072 |

where *X _{g,f}* is the power output of generator

*g*using fuel

*f*, in MW. We’ll make a subtle distinction between the power specific to a generator and fuel (X), and power that’s summed over one or both of these factors (P). The power output for generator

*g*,

*P*, is then just the sum over the fuels

_{g}*f: P*. The total output power

_{g}= X_{g,FO}+ X_{g,BFG}*P*is just the sum of P

_{tot}_{g}over generators,

*P*. Our total amount of fuel purchased is

_{1}+P_{2}*F*.

_{tot}= F_{BFG}+ F_{FO}Formally, the table of coefficients above reflects a 3^{rd} rank tensor—two of the ranks are compressed in the row indices (the repeated index values being the giveaway). This encoding will manifest in how we encode higher rank tensors in GAMS. One dimension corresponds to the generator number; a second dimension, to the fuel; and the last dimension, to the ordinality of the exponent on the power produced. Here, we are limited to either 0 (the constant term each cell of the right column) or 1 (the X^{1} term). In the QP example below, we will extend this to 2 (i.e., X^{2}). So, to express how much of a particular fuel F_{f} is consumed, we take the inner product of the third-rank coefficient tensor *A _{fge}* and the second-rank tensor

*X*. However much fuel we use, it must be greater than or equal to this inner product:

_{ge}The power actually generated also has to be at least *P _{req}* (50 MW),

Finally, the power output of each generator has to fall between its minimum and maximum limits:

We now have all the information we need to formulate a linear programming optimization in GAMS. Let’s go through the GAMS input file in [1] line-by-line to understand the translation from the math and ideas above to an executable program.

The input file starts with a title.

$TITLE Power Generation via Fuel Oil

It’s helpful to your future self to include a more extended explanation of what’s being attempted. GAMS permits text blocks using the $ontext and $offtext tags.

$ontext

This file contains the basic data and definition of the

example optimization problem found in

Chen, X, et al. (1996) "Comparison of GAMS, AMPL, and MINOS for

Optimization" Chem. Eng. Edu. (Summer): 220-227.

with a linearized fuel consumption relationship to power

to make into an LP problem.

$offtext

The next section defines sets of objects—these are essentially the things that will be indexed over in the problem formulation.

$stitle set definition

sets

G Power Generators /gen1*gen2/

F Fuels /oil,gas/

K Constants in Fuel Consumption Equations/0*1/;

Several features of GAMS are evident in the above.

- A unit of GAMS input begins with a keyword, like “sets,” and ends with a semicolon. The input is otherwise fairly freeform, with the exception that tabs are not allowed without some further definitions.
- Labels follow the formal objects (set names, here) by a variable number of spaces.
- Elements of the composite object (set) are named between slash characters, separated by either commas or asterisks. Commas are used for explicit enumeration of set elements, whereas asterisks denote ranges. GAMS is quite intelligent in creating ranges and is able to ferret out the part of the expression which can be translated into a range. Thus, set G is {gen1, gen2}; set F is {oil,gas}; and, set K is {0,1}.

Set K corresponds with our index e (“exponent”) in the formal expression.

The third-rank tensor A is expressed in the following table:

TABLE A(G,F,K) Coefficients in the fuel consumption equations

0 1

gen1.oil 1.4609 .18223

gen1.gas 1.5742 .19572

gen2.oil 0.8008 .24372

gen2.gas 0.7266 .27072;

Given our limitation of two dimensions on a written page or computer screen, expressing any third or higher rank tensor requires combining indices along one or both dimensions. Here, our indices *g* and *f* (sets G and F) are combined in rows, with the exponent *e* (set K) of the power production *X _{gfe}* serving as column headings. Note that the row identifiers are of the form

*g.f*, with each index possibility having been defined via our set definitions. The TABLE declaration also illustrates a property of GAMS: values are associated with indices by virtue of overlapping spacing. Thus, “1.4609” is associated with

*A*because it is underneath “0”. If we were using tab characters, GAMS would be unable to determine whether the values shared a column with the heading or not (although one can define a fixed tab width in GAMS if tabs are insisted upon).

_{1,FO,0}The next declaration in the GAMS input is of constants.

PARAMETER PMAX(G) Maximum power outputs of generators

/gen1 30.0, gen2 25.0/;

PARAMETER PMIN(G) Minimum power outputs of generators

/gen1 18.0, gen2 14.0/;

SCALAR GASSUP Maximum supply of Blast Furnace Gas in units per h

/10.0/

PREQ Total power output required in MW

/50.0/;

Statements take the form of a data type, a name (with an associated set indicator where appropriate), a free text label, and an array of values. PARAMETERs are one-dimensional arrays with an arbitrary number of elements; SCALARs are zero-dimensional arrays. PREQ is a SCALAR, since it is part of the same declaration as GASSUP (no semicolon after /10.0/).

Next is a statement of the quantities that can vary during the optimization.

VARIABLES P(G) Total power output of generators in MW

X(G,F) Power outputs of generators from specific fuels

Z(F) Total amounts of fuel purchased

OILPUR Total amount of fuel oil purchased;

POSITIVE VARIABLES P, X, Z;

The last statement corresponds to 3 constraints: P ≥ 0, X ≥ 0, and Z ≥ 0. OILPUR is an element of Z, as will be seen, and thus implicitly receives Z’s constraint as well.

The next set of statements form the meat of the optimization problem, expressing the objective function equation as well as the equality and inequality constraints.

EQUATIONS TPOWER Required power must be generated

PWR(G) Power generated by individual generators

OILUSE Amount of oil purchased to be minimized

FUELUSE(F) Fuel usage must not exceed purchase;

TPOWER.. SUM(G,P(G))=G=PREQ;

PWR(G).. P(G)=E=SUM(F,X(G,F));

FUELUSE(F).. Z(F)=G=SUM(G,a(G,F,"0") + a(G,F,"1")*X(G,F));

OILUSE.. OILPUR=E=Z("OIL");

GAMS input requires first a simple declaration of the “equations” (really, relations, since inequalities are included), their names (left-hand sides), and set dependencies. Each declared relation is then defined explicitly. The operator “=G=” corresponds to “≥” , and “=E=” with “=”.The SUM operator takes the form SUM(indices, summand); so, FUELUSE in this example translates to

Note that explicit indices must be quoted in the expression. OILUSE expresses that the amount of fuel oil purchased is equal to the “OIL” element of Z. Note that GAMS input is not case-sensitive—“OIL” and “oil” reference the same thing.

Final constraints to be made are the upper and lower bounds of power production by the generators, and the maximum supply of BFG available.

P.UP(G) = PMAX(G);

P.LO(G) = PMIN(G);

Z.UP("gas") = GASSUP;

Each variable in the system can take an upper and lower bound. These are expressed as the attributes “UP” and “LO” attributes of the variable in GAMS. Here, each generator in the set G is assigned bounds by referencing the maximum and minimum power generation for each coded earlier.

In order to begin the optimization, the independent variables of power production need to be seeded, in this case simply with the average of the maximum and minimum limits on P:

`P.L(G)=.5*(PMAX(G)+PMIN(G));`

The MODEL statement in GAMS is a means of grouping equations to solve—one can in principle have multiple models in the same file, but here we only have a single model. If one did have multiple models, it would be necessary to reference the relation names to associate certain ones with their respective models. Here, the keyword “all” is used.

`MODEL FUELOIL/all/;`

Finally, the GAMS runtime is instructed to solve the (or one or more of the) models.

`SOLVE FUELOIL USING LP MINIMIZING OILPUR;`

The file containing these statements comprises the definition of the model(s) that GAMS will solve. In order to run the optimization, issue the following command from the shell:

`[user@login1 gams_tests]$ gams model.gms`

The solver output is found in the file with the same basename as the model file, but an “lst” extension. In order to examine the model definitions in a clearer form than expressed in the solver output, you can re-define what “LP” (or whatever class of solver you’re using) at the command line:

`[user@login1 gams_tests]$ gams model.gms lp=convert`

The “convert” solver just parses the model, and creates files dict.txt (containing the mapping of user-defined equations and variables) and gams.gms (containing the definitions of these variables and equations after parameter substitutions, etc.). This same mechanism can be used to change the solver used; for example, to use the CPLEX solver in an LP problem rather than the default Gurobi solver,

`[user@login1 gams_tests]$ gams model.gms lp=cplex`

**Adding non-linearity**

In the above example, we modeled fuel usage as a linear function of power produced. If the actual fuel usage were not linear, we could model the actual dependence as a polynomial fit. For example, the figure below shows the BFG and fuel oil relationships for the linear model solved for above, and the original quadratic model in reference 1.

We can convert the linear model to the quadratic one with just a few changes.

- Change the definition of the set K to include 2:
`K Constants in Fuel Consumption Equations/0,1,2/;`

- Change the TABLE “a” to include the quadratic coefficients:
TABLE A(G,F,K) Coefficients in the fuel consumption equations

0 1 2

gen1.oil 1.4609 .15186 .00145

gen1.gas 1.5742 .16310 .001358

gen2.oil 0.8008 .20310 .000916

gen2.gas 0.7266 .22560 .000778; - Change the definition of FUELUSE to express the quadratic relationship:
FUELUSE(F).. Z(F)=G=SUM((K,G),a(G,F,K)*X(G,F)**(ORD(K)-1));

This definition differs a bit from the linear version, in that we are not using explicit indices. Instead, we treat “K” as a summation index, and use the “ORD” function to return the index of the current K set member. As GAMS uses 1-indexing, this is just a means of expressing {0,1,2}. We can’t use the value of K directly, as the values are not treated as numbers by GAMS. FUELUSE then translates to

- Finally, we only need to change the solver class from LP to NLP,
SOLVE FUELOIL USING NLP MINIMIZING OILPUR;

**Moving Ahead**

The above only touches on GAMS capabilities, both in the problem size and complexity. To use GAMS for your own problems on Peregrine, simply load the “gams” module to set up the environment.

**References**

[1] X Chen, KS Rao, J Yu & RW Pike (1996) “Comparison of GAMS, AMPL, and MINOS for Optimization.” *Chemical Engineering Education* (Summer): 220-227.