Loading...
Searching...
No Matches
run_engine.py
Go to the documentation of this file.
11
12from io import StringIO
13import json
14import os
15import sys
16from threading import Thread
17import time
18from gams import GamsWorkspace, GamsEngineConfiguration, SolveLink
19
20
21GAMS_DATA = """
22Set
23 i 'canning plants' / seattle, san-diego /
24 j 'markets' / new-york, chicago, topeka /;
25
26Parameter
27 a(i) 'capacity of plant i in cases'
28 / seattle 350
29 san-diego 600 /
30
31 b(j) 'demand at market j in cases'
32 / new-york 325
33 chicago 300
34 topeka 275 /;
35
36Table d(i,j) 'distance in thousands of miles'
37 new-york chicago topeka
38 seattle 2.5 1.7 1.8
39 san-diego 2.5 1.8 1.4;
40
41Scalar
42 f 'freight in dollars per case per thousand miles' / 90 /
43 bmult 'demand multiplier' / 1 /;
44"""
45
46GAMS_MODEL = """
47Set
48 i 'canning plants'
49 j 'markets';
50
51Parameter
52 a(i) 'capacity of plant i in cases'
53 b(j) 'demand at market j in cases'
54 d(i,j) 'distance in thousands of miles';
55
56Scalar
57 f 'freight in dollars per case per thousand miles'
58 bmult 'demand multiplier';
59
60$if not set gdxincname $abort 'no include file name for data file provided'
61$gdxIn %gdxincname%
62$load i j a b d f bmult
63$gdxIn
64
65$echo "test" > test.txt
66
67Parameter c(i,j) 'transport cost in thousands of dollars per case';
68c(i,j) = f*d(i,j)/1000;
69
70Variable
71 x(i,j) 'shipment quantities in cases'
72 z 'total transportation costs in thousands of dollars';
73
74Positive Variable x;
75
76Equations
77 cost 'define objective function'
78 supply(i) 'observe supply limit at plant i'
79 demand(j) 'satisfy demand at market j';
80
81cost.. z =e= sum((i,j), c(i,j)*x(i,j));
82
83supply(i).. sum(j, x(i,j)) =l= a(i);
84
85demand(j).. sum(i, x(i,j)) =g= bmult*b(j);
86
87Model transport /all/;
88
89solve transport using lp minimizing z;
90
91Scalar
92 ms 'model status'
93 ss 'solve status';
94
95display x.l, x.m;
96"""
97
98if __name__ == "__main__":
99 sys_dir = sys.argv[1] if len(sys.argv) > 1 else None
100 ws = GamsWorkspace(system_directory=sys_dir)
101
102 # set up configuration required for any job on GAMS Engine
103
104 engine_configuration = GamsEngineConfiguration(
105 host=os.environ["ENGINE_URL"],
106 username=os.environ["ENGINE_USER"],
107 password=os.environ["ENGINE_PASSWORD"],
108 namespace=os.environ["ENGINE_NAMESPACE"],
109 )
110
111 job = ws.add_job_from_string(GAMS_DATA)
112 job.run_engine(engine_configuration)
113 gdx_file_path = os.path.join(ws.working_directory, "tdata.gdx")
114 job.out_db.export(gdx_file_path)
115 job = ws.add_job_from_string(GAMS_MODEL)
116
117 opt = ws.add_options()
118 opt.defines["gdxincname"] = "tdata"
119 opt.all_model_types = "xpress"
120 job.run_engine(
121 engine_configuration,
122 extra_model_files=gdx_file_path,
123 engine_options={
124 "inex_string": json.dumps({"type": "include", "files": ["*.gdx"]})
125 },
126 gams_options=opt,
127 )
128 expected_levels = {
129 ("seattle", "new-york"): 0.0,
130 ("seattle", "chicago"): 300.0,
131 ("seattle", "topeka"): 0.0,
132 ("san-diego", "new-york"): 325.0,
133 ("san-diego", "chicago"): 0.0,
134 ("san-diego", "topeka"): 275.0,
135 }
136
137 for rec in job.out_db["x"]:
138 print(
139 f"x({rec.key(0)},{rec.key(1)}): level={rec.level} marginal={rec.marginal}"
140 )
141 if expected_levels[tuple(rec.keys)] != rec.level:
142 raise Exception("Unexpected results.")
143
144 if os.path.exists(os.path.join(ws.working_directory, "test.txt")):
145 raise Exception("Did not expected to find 'test.txt' written by GAMS model supposed to run remotely.")
146
147 cp = ws.add_checkpoint()
148
149 job_a = ws.add_job_from_string(GAMS_DATA)
150 job_a.run_engine(engine_configuration)
151 opt.defines["gdxincname"] = job_a.out_db.name
152
153 job_b = ws.add_job_from_string(GAMS_MODEL)
154 job_b.run_engine(
155 engine_configuration, gams_options=opt, databases=job_a.out_db, checkpoint=cp
156 )
157
158 for rec in job.out_db["x"]:
159 if expected_levels[tuple(rec.keys)] != rec.level:
160 raise Exception("Unexpected results.")
161
162 expected_bmult = [
163 {"bmult": 0.9, "ms": 1, "ss": 1, "obj": 138.31},
164 {"bmult": 1.2, "ms": 4, "ss": 1, "obj": 184.41},
165 ]
166
167 # create a new GamsJob that is initialized from the GamsCheckpoint
168 for scen in expected_bmult:
169 job = ws.add_job_from_string(
170 f"bmult={scen['bmult']}; solve transport min z use lp; ms=transport.modelstat; ss=transport.solvestat;",
171 cp,
172 )
173 job.run_engine(engine_configuration)
174 print(f"Scenario bmult={scen['bmult']}:")
175 print(f" Modelstatus: {job.out_db['ms'].find_record().value}")
176 print(f" Solvestatus: {job.out_db['ss'].find_record().value}")
177 print(f" Obj: {job.out_db['z'].find_record().level}")
178
179 if job.out_db["bmult"].find_record().value != scen["bmult"]:
180 raise Exception("Unexpected bmult.")
181 if job.out_db["ms"].find_record().value != scen["ms"]:
182 raise Exception("Unexpected model status.")
183 if job.out_db["ss"].find_record().value != scen["ss"]:
184 raise Exception("Unexpected solve status.")
185 if round(job.out_db["z"].find_record().level, 2) != scen["obj"]:
186 raise Exception("Unexpected obj.")
187
188 # example how to interrupt Engine job
189
190 ws.gamslib("clad")
191 job = ws.add_job_from_file("clad")
192
193 # define an option file for the solver to be used
194 option_file1_path = os.path.join(ws.working_directory, "cplex.opt")
195 with open(option_file1_path, "w") as f:
196 # set relative stopping tolerance to 0 initially
197 f.write("epgap 0\n")
198 # activate interactive option setting on interrupt
199 f.write("interactive 1\n")
200 # define new option file to read on interrupt
201 f.write("iafile cplex.op2\n")
202
203 # write new Cplex option file
204 option_file2_path = os.path.join(ws.working_directory, "cplex.op2")
205 with open(option_file2_path, "w") as f:
206 f.write("epgap 0.1")
207
208 opt = ws.add_options()
209 opt.mip = "cplex"
210 opt.optfile = 1
211 opt.solvelink = SolveLink.LoadLibrary
212
213 sw = StringIO()
214
215 # run GamsJob job in separate thread
216 opt_thread = Thread(
217 target=job.run_engine,
218 args=(engine_configuration,),
219 kwargs={
220 "output": sw,
221 "extra_model_files": [option_file1_path, option_file2_path, "claddat.gdx"],
222 "gams_options": opt,
223 },
224 )
225 opt_thread.start()
226
227 while True:
228 if sw.tell() == 0:
229 time.sleep(0.5)
230 continue
231 job.interrupt()
232 print("Interrupted Cplex to continue with new option")
233 break
234
235 # if job is still running, wait until it is finished
236 if opt_thread.is_alive():
237 opt_thread.join()
238 # check if everything worked as expected
239 log = sw.getvalue()
240 if not "Interrupted..." in log:
241 raise Exception("Expected the solver to be interrupted at least once.")