Loading...
Searching...
No Matches
run_engine.py
Go to the documentation of this file.
1
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 work_dir = sys.argv[2] if len(sys.argv) > 2 else None
101 ws = GamsWorkspace(system_directory=sys_dir, working_directory=work_dir)
102
103 # set up configuration required for any job on GAMS Engine
104
105 engine_configuration = GamsEngineConfiguration(
106 host=os.environ["ENGINE_URL"],
107 username=os.environ["ENGINE_USER"],
108 password=os.environ["ENGINE_PASSWORD"],
109 namespace=os.environ["ENGINE_NAMESPACE"],
110 )
111
112 job = ws.add_job_from_string(GAMS_DATA)
113 job.run_engine(engine_configuration)
114 gdx_file_path = os.path.join(ws.working_directory, "tdata.gdx")
115 job.out_db.export(gdx_file_path)
116 job = ws.add_job_from_string(GAMS_MODEL)
117
118 opt = ws.add_options()
119 opt.defines["gdxincname"] = "tdata"
120 opt.all_model_types = "xpress"
121 job.run_engine(
122 engine_configuration,
123 extra_model_files=gdx_file_path,
124 engine_options={
125 "inex_string": json.dumps({"type": "include", "files": ["*.gdx"]})
126 },
127 gams_options=opt,
128 )
129 expected_levels = {
130 ("seattle", "new-york"): 0.0,
131 ("seattle", "chicago"): 300.0,
132 ("seattle", "topeka"): 0.0,
133 ("san-diego", "new-york"): 325.0,
134 ("san-diego", "chicago"): 0.0,
135 ("san-diego", "topeka"): 275.0,
136 }
137
138 for rec in job.out_db["x"]:
139 print(
140 f"x({rec.key(0)},{rec.key(1)}): level={rec.level} marginal={rec.marginal}"
141 )
142 if expected_levels[tuple(rec.keys)] != rec.level:
143 raise Exception("Unexpected results.")
144
145 if os.path.exists(os.path.join(ws.working_directory, "test.txt")):
146 raise Exception(
147 "Did not expected to find 'test.txt' written by GAMS model supposed to run remotely."
148 )
149
150 cp = ws.add_checkpoint()
151
152 job_a = ws.add_job_from_string(GAMS_DATA)
153 job_a.run_engine(engine_configuration)
154 opt.defines["gdxincname"] = job_a.out_db.name
155
156 job_b = ws.add_job_from_string(GAMS_MODEL)
157 job_b.run_engine(
158 engine_configuration, gams_options=opt, databases=job_a.out_db, checkpoint=cp
159 )
160
161 for rec in job.out_db["x"]:
162 if expected_levels[tuple(rec.keys)] != rec.level:
163 raise Exception("Unexpected results.")
164
165 expected_bmult = [
166 {"bmult": 0.9, "ms": 1, "ss": 1, "obj": 138.31},
167 {"bmult": 1.2, "ms": 4, "ss": 1, "obj": 184.41},
168 ]
169
170 # create a new GamsJob that is initialized from the GamsCheckpoint
171 for scen in expected_bmult:
172 job = ws.add_job_from_string(
173 f"bmult={scen['bmult']}; solve transport min z use lp; ms=transport.modelstat; ss=transport.solvestat;",
174 cp,
175 )
176 job.run_engine(engine_configuration)
177 print(f"Scenario bmult={scen['bmult']}:")
178 print(f" Modelstatus: {job.out_db['ms'].find_record().value}")
179 print(f" Solvestatus: {job.out_db['ss'].find_record().value}")
180 print(f" Obj: {job.out_db['z'].find_record().level}")
181
182 if job.out_db["bmult"].find_record().value != scen["bmult"]:
183 raise Exception("Unexpected bmult.")
184 if job.out_db["ms"].find_record().value != scen["ms"]:
185 raise Exception("Unexpected model status.")
186 if job.out_db["ss"].find_record().value != scen["ss"]:
187 raise Exception("Unexpected solve status.")
188 if round(job.out_db["z"].find_record().level, 2) != scen["obj"]:
189 raise Exception("Unexpected obj.")
190
191 # example how to interrupt Engine job
192
193 ws.gamslib("clad")
194 job = ws.add_job_from_file("clad")
195
196 # define an option file for the solver to be used
197 option_file1_path = os.path.join(ws.working_directory, "cplex.opt")
198 with open(option_file1_path, "w") as f:
199 # set relative stopping tolerance to 0 initially
200 f.write("epgap 0\n")
201 # activate interactive option setting on interrupt
202 f.write("interactive 1\n")
203 # define new option file to read on interrupt
204 f.write("iafile cplex.op2\n")
205
206 # write new Cplex option file
207 option_file2_path = os.path.join(ws.working_directory, "cplex.op2")
208 with open(option_file2_path, "w") as f:
209 f.write("epgap 0.1")
210
211 opt = ws.add_options()
212 opt.mip = "cplex"
213 opt.optfile = 1
214 opt.solvelink = SolveLink.LoadLibrary
215
216 sw = StringIO()
217
218 # run GamsJob job in separate thread
219 opt_thread = Thread(
220 target=job.run_engine,
221 args=(engine_configuration,),
222 kwargs={
223 "output": sw,
224 "extra_model_files": [option_file1_path, option_file2_path, "claddat.gdx"],
225 "gams_options": opt,
226 },
227 )
228 opt_thread.start()
229
230 while True:
231 if sw.tell() == 0:
232 time.sleep(0.5)
233 continue
234 job.interrupt()
235 print("Interrupted Cplex to continue with new option")
236 break
237
238 # if job is still running, wait until it is finished
239 if opt_thread.is_alive():
240 opt_thread.join()
241 # check if everything worked as expected
242 log = sw.getvalue()
243 if not "Interrupted..." in log:
244 raise Exception("Expected the solver to be interrupted at least once.")