Loading...
Searching...
No Matches
transportEngine.cpp
Go to the documentation of this file.
1
25#include "gams.h"
26#include "gamsengineconfiguration.h"
27#include "gamsvariablerecord.h"
28
29#include <cmath>
30#include <iostream>
31#include <fstream>
32#include <filesystem>
33#include <exception>
34#include <gamspath.h>
35
36#ifdef _WIN32
37#include <stdlib.h>
38#endif
39
40using namespace gams;
41using namespace std;
42using namespace std::string_literals;
43
44static string getDataText()
45{
46 string data {
47 R"(
48Sets
49 i canning plants / seattle, san-diego /
50 j markets / new-york, chicago, topeka / ;
51
52Parameters
53
54 a(i) capacity of plant i in cases
55 / seattle 350
56 san-diego 600 /
57
58 b(j) demand at market j in cases
59 / new-york 325
60 chicago 300
61 topeka 275 / ;
62
63Table d(i,j) distance in thousands of miles
64 new-york chicago topeka
65 seattle 2.5 1.7 1.8
66 san-diego 2.5 1.8 1.4 ;
67
68Scalar f freight in dollars per case per thousand miles / 90 /
69 bmult demand multiplier / 1 /;
70 )"s
71 };
72 return data;
73}
74
75static string getModelText()
76{
77 string model {
78 R"(
79Sets
80 i canning plants
81 j markets;
82
83Parameters
84 a(i) capacity of plant i in cases
85 b(j) demand at market j in cases
86 d(i,j) distance in thousands of miles
87Scalar f freight in dollars per case per thousand miles
88 bmult demand multiplier;
89
90$if not set gdxincname $abort 'no include file name for data file provided'
91$gdxLoad %gdxincname% i j a b d f bmult
92
93$echo test > test.txt
94
95Parameter c(i,j) transport cost in thousands of dollars per case ;
96 c(i,j) = f * d(i,j) / 1000 ;
97
98Variables
99 x(i,j) shipment quantities in cases
100 z total transportation costs in thousands of dollars ;
101
102Positive Variable x ;
103
104Equations
105 cost define objective function
106 supply(i) observe supply limit at plant i
107 demand(j) satisfy demand at market j ;
108
109cost .. z =e= sum((i,j), c(i,j)*x(i,j)) ;
110supply(i) .. sum(j, x(i,j)) =l= a(i) ;
111demand(j) .. sum(i, x(i,j)) =g= bmult*b(j) ;
112
113Model transport /all/ ;
114
115Solve transport using lp minimizing z ;
116
117Scalar ms 'model status', ss 'solve status';
118
119Display x.l, x.m ;
120 )"s
121 };
122 return model;
123}
124
134int main(int argc, char* argv[])
135{
136 cout << "---------- Transport Engine --------------" << endl;
137
138 GAMSWorkspaceInfo wsInfo;
139 if (argc > 1)
140 wsInfo.setSystemDirectory(argv[1]);
141 GAMSWorkspace ws(wsInfo);
142
143 filesystem::remove(ws.workingDirectory().append("/test.txt"));
144
145 string envValues[4] = {};
146
147 string envPrefix("ENGINE_");
148 int index = 0;
149 for (const string &id : {"URL", "USER", "PASSWORD", "NAMESPACE"}) {
150
151#ifdef _WIN32
152 char *buffer;
153 size_t len;
154 errno_t err = _dupenv_s( &buffer, &len, (envPrefix + id).c_str() );
155 if (err == 0 && buffer) {
156 envValues[index++] = string(buffer);
157 free(buffer);
158 } else {
159 free(buffer);
160 cerr << "No ENGINE_" << id << " set" << endl;
161 return -1;
162 }
163#else
164 const char* value = getenv((envPrefix + id).c_str());
165 if(!value) {
166 cerr << "No ENGINE_" << id << " set" << endl;
167 return -1;
168 } else {
169 envValues[index++] = value;
170 }
171#endif
172 }
173
174 GAMSEngineConfiguration engineConf(envValues[0], envValues[1],
175 envValues[2], envValues[3]);
176
177 // run with data from a string with GAMS syntax with explicit export to GDX file
178 GAMSJob jobData = ws.addJobFromString(getDataText());
179 GAMSOptions defaultOptions = ws.addOptions();
180
181 cout << "Run 1" << endl;
182 jobData.runEngine(engineConf, &defaultOptions, nullptr, &cout);
183 jobData.outDB().doExport(filesystem::absolute(ws.workingDirectory().append("/tdata.gdx")).string());
184
185 map<string, double> expectedLevels = { { "seattle.new-york", 0.0 },
186 { "seattle.chicago", 300.0 },
187 { "seattle.topeka", 0.0 },
188 { "san-diego.new-york", 325.0 },
189 { "san-diego.chicago", 0.0 },
190 { "san-diego.topeka", 275.0 }
191 };
192
193
194 GAMSJob jobModel = ws.addJobFromString(getModelText());
195
196 GAMSOptions opt = ws.addOptions();
197 opt.setDefine("gdxincname", "tdata.gdx");
198 opt.setAllModelTypes("xpress");
199
200 // run a job using an instance of GAMSOptions that defines the data include file
201 cout << "Run 2" << endl;
202 jobModel.runEngine(engineConf, &opt, nullptr, &cout, {}, { "tdata.gdx" },
203 {
204 { "inex_string", "{\"type\": \"include\", \"files\": [\"*.gdx\"]}" }
205 });
206
207 for (GAMSVariableRecord &rec : jobModel.outDB().getVariable("x")) {
208 cout << "x(" << rec.key(0) << "," + rec.key(1) << "): level=" << rec.level()
209 << "marginal=" << rec.marginal() << "\n";
210
211 if (expectedLevels.at(rec.key(0).append(".").append(rec.key(1))) != rec.level()) {
212 cout << "Unexpected result, expected level: "
213 << expectedLevels.at(rec.key(0).append(".").append(rec.key(1)))
214 << endl;
215 return 1;
216 }
217 }
218
219 if (filesystem::exists(filesystem::path(ws.workingDirectory()).append("test.txt"))) {
220 cout << "Test.txt should not have sent back - inex_string failed" << endl;
221 return 1;
222 }
223
224 // same but with implicit database communication
225 GAMSCheckpoint cp = ws.addCheckpoint();
226 {
227 GAMSOptions opt = ws.addOptions();
228 GAMSJob jobA = ws.addJobFromString(getDataText());
229 GAMSJob jobB = ws.addJobFromString(getModelText());
230
231 cout << "Run 3" << endl;
232 jobA.runEngine(engineConf, &defaultOptions, nullptr, &cout);
233
234 opt.setDefine("gdxincname", jobA.outDB().name());
235 opt.setAllModelTypes("xpress");
236
237 cout << "Run 4" << endl;
238 jobB.runEngine(engineConf, &opt, &cp, &cout, vector<GAMSDatabase>{jobA.outDB()});
239
240 for (GAMSVariableRecord &rec : jobB.outDB().getVariable("x")) {
241 cout << "x(" << rec.key(0) << "," + rec.key(1) << "): level=" << rec.level()
242 << "marginal=" << rec.marginal() << "\n";
243
244 if (expectedLevels.at(rec.key(0).append(".").append(rec.key(1))) != rec.level()) {
245 cout << "Unexpected result, expected level: "
246 << expectedLevels.at(rec.key(0).append(".").append(rec.key(1)))
247 << endl;
248 return 1;
249 }
250 }
251 vector<map<string, double>> bmultExpected {
252 {{ "bmult", 0.9 }, { "ms", 1 }, { "ss", 1 }, { "obj", 138.31 } },
253 {{ "bmult", 1.2 }, { "ms", 4 }, { "ss", 1 }, { "obj", 184.41 } }
254 };
255
256 for (map<string, double> &m : bmultExpected) {
257 GAMSJob tEbmult = ws.addJobFromString("bmult=" + to_string(m["bmult"])
258 + "; solve transport min z use lp; ms=transport.modelstat; "
259 "ss=transport.solvestat;", cp);
260 try {
261 cout << "Run 5" << endl;
262 tEbmult.runEngine(engineConf, &defaultOptions, nullptr, &cout);
263 cout << "Scenario bmult=" << to_string(m["bmult"]) << ":" << endl;
264 cout << " Modelstatus: " << tEbmult.outDB().getParameter("ms").firstRecord().value() << endl;
265 cout << " Solvestatus: " << tEbmult.outDB().getParameter("ss").firstRecord().value() << endl;
266 cout << " Obj : " << tEbmult.outDB().getVariable("z").firstRecord().level() << endl;
267 if (tEbmult.outDB().getParameter("bmult").firstRecord().value() != m["bmult"]) {
268 cout << "Unexpected input, expected bmult: " + to_string(m["bmult"]) << endl;
269 return 1;
270 }
271 if (tEbmult.outDB().getParameter("ms").firstRecord().value() != m["ms"]) {
272 cout << "Unexpected result, expected ms: " + to_string(m["ms"]);
273 return 1;
274 }
275 if (tEbmult.outDB().getParameter("ss").firstRecord().value() != m["ss"]) {
276 cout << "Unexpected result, expected ss: " + to_string(m["ss"]);
277 return 1;
278 }
279 if (fabs(tEbmult.outDB().getVariable("z").firstRecord().level() - m["obj"]) > 0.01) {
280 cout << "Unexpected result, expected obj: " + to_string(m["obj"]);
281 return 1;
282 }
283
284 }
285 catch (exception ex) {
286 cout << "Exception caught:" << ex.what() << endl;
287 return 1;
288 }
289 }
290 }
291
292 // Example how to interrupt Engine Job
293 GAMSJob jc = ws.addJobFromGamsLib("clad");
294
295 // Define an option file for the solver to be used
296 string optFile1Path = filesystem::path(ws.workingDirectory().append("/cplex.opt")).string();
297 {
298 ofstream optFile1(optFile1Path);
299
300 // Set relative stopping tolerance to 0 initially
301 optFile1 << "epgap 0" << endl;
302 // Activate interactive option setting on interrupt
303 optFile1 << "interactive 1" << endl;
304 // Define new option file to read on interrupt
305 optFile1 << "iafile cplex.op2" << endl;
306 }
307
308 // Write new Cplex option file
309 string optFile2Path = filesystem::path(ws.workingDirectory().append("/cplex.op2")).string();
310 {
311 ofstream optFile2(filesystem::path(ws.workingDirectory().append("/cplex.op2")));
312 optFile2.open(optFile2Path);
313 optFile2 << "epgap 0.1" << endl;
314 optFile2.close();
315 }
316
317 opt = ws.addOptions();
318
319 opt.setMIP("cplex");
320 opt.setOptFile(1);
322
323 cout << "Run 6" << endl;
324
325 string logPath = filesystem::path(ws.workingDirectory().append("/ej.log")).string();
326 {
327 ofstream logFile(logPath);
328 thread optThread(&GAMSJob::runEngine, &jc, engineConf, &opt, nullptr, &logFile,
329 vector<GAMSDatabase>(), set<string> {optFile1Path, optFile2Path, "claddat.gdx"},
330 unordered_map<string, string>(), true, true);
331
332 while (filesystem::exists(logPath) && filesystem::is_empty(logPath))
333 this_thread::sleep_for(500ms);
334
335 jc.interrupt();
336 cout << "Interrupted Cplex to continue with new option" << endl;
337
338 if (optThread.joinable())
339 optThread.join();
340 }
341
342 {
343 string line;
344 ifstream inLog(logPath);
345 while (getline(inLog, line)) {
346 if (line.find("Interrupted") != string::npos) {
347 cout << "Interrupted succesfully" << endl;
348 return 0;
349 }
350 }
351 cout << "Expected the solver to be interrupted at least once." << endl;
352 return 1;
353 }
354
355 cout << "Interrupted succesfully" << endl;
356
357 return 0;
358}
void doExport(const std::string &filePath="")
GAMSParameter getParameter(const std::string &name)
std::string name()
GAMSVariable getVariable(const std::string &name)
void runEngine(const GAMSEngineConfiguration &engineConfiguration, GAMSOptions *gamsOptions, gams::GAMSCheckpoint *checkpoint, std::ostream *output=nullptr, const std::vector< gams::GAMSDatabase > &databases={}, const std::set< std::string > &extraModelFiles={}, const std::unordered_map< std::string, std::string > &engineOptions={}, bool createOutDB=true, bool removeResults=true)
bool interrupt()
GAMSDatabase outDB()
void setAllModelTypes(const std::string &solver)
void setMIP(const std::string &value)
void setDefine(const std::string &key, const std::string &value)
void setSolveLink(const GAMSOptions::ESolveLink::ESolveLinkEnum value)
void setOptFile(const int value)
GAMSParameterRecord firstRecord(const std::vector< std::string > &slice)
GAMSVariableRecord firstRecord(const std::vector< std::string > &slice)
void setSystemDirectory(std::string systemDir)