TraDemGen Logo  1.00.0
C++ Simulated Travel Demand Generation Library
 All Classes Namespaces Files Functions Variables Typedefs Friends Pages
trademgen_generateDemand.cpp
Go to the documentation of this file.
1 // //////////////////////////////////////////////////////////////////////
2 // Import section
3 // //////////////////////////////////////////////////////////////////////
4 // STL
5 #include <cassert>
6 #include <sstream>
7 #include <fstream>
8 #include <vector>
9 #include <list>
10 #include <string>
11 // //// Boost (Extended STL) ////
12 // Boost Tokeniser
13 #include <boost/tokenizer.hpp>
14 // Boost Program Options
15 #include <boost/program_options.hpp>
16 // Boost Accumulators
17 #include <boost/accumulators/accumulators.hpp>
18 #include <boost/accumulators/statistics.hpp>
19 // Boost Progress
20 //#include <boost/progress.hpp>
21 // StdAir
22 #include <stdair/stdair_basic_types.hpp>
23 #include <stdair/basic/BasConst_General.hpp>
24 #include <stdair/basic/ProgressStatusSet.hpp>
25 #include <stdair/basic/DemandGenerationMethod.hpp>
26 #include <stdair/bom/EventStruct.hpp>
27 #include <stdair/bom/BookingRequestStruct.hpp>
28 #include <stdair/bom/BomDisplay.hpp>
29 #include <stdair/service/Logger.hpp>
30 // TraDemGen
32 #include <trademgen/config/trademgen-paths.hpp>
33 
34 // Aliases for namespaces
35 namespace ba = boost::accumulators;
36 
37 // //////// Specific type definitions ///////
38 typedef unsigned int NbOfRuns_T;
39 
43 typedef ba::accumulator_set<double,
44  ba::stats<ba::tag::min, ba::tag::max,
45  ba::tag::mean (ba::immediate),
46  ba::tag::sum,
47  ba::tag::variance> > stat_acc_type;
48 
49 // //////// Constants //////
53 const stdair::Filename_T K_TRADEMGEN_DEFAULT_LOG_FILENAME ("trademgen_generateDemand.log");
54 
58 const stdair::Filename_T K_TRADEMGEN_DEFAULT_INPUT_FILENAME (STDAIR_SAMPLE_DIR
59  "/demand01.csv");
60 
64 const stdair::Filename_T K_TRADEMGEN_DEFAULT_OUTPUT_FILENAME ("request.csv");
65 
69 const stdair::DemandGenerationMethod
71  stdair::DemandGenerationMethod::POI_PRO;
72 
78 
82 const stdair::RandomSeed_T K_TRADEMGEN_DEFAULT_RANDOM_SEED =
83  stdair::DEFAULT_RANDOM_SEED;
84 
89 
95 
100 
101 
105 void stat_display (std::ostream& oStream, const stat_acc_type& iStatAcc) {
106 
107  // Store current formatting flags of the output stream
108  std::ios::fmtflags oldFlags = oStream.flags();
109 
110  //
111  oStream.setf (std::ios::fixed);
112 
113  //
114  oStream << "Statistics for the demand generation runs: " << std::endl;
115  oStream << " minimum = " << ba::min (iStatAcc) << std::endl;
116  oStream << " mean = " << ba::mean (iStatAcc) << std::endl;
117  oStream << " maximum = " << ba::max (iStatAcc) << std::endl;
118  oStream << " count = " << ba::count (iStatAcc) << std::endl;
119  oStream << " variance = " << ba::variance (iStatAcc) << std::endl;
120 
121  // Reset formatting flags of output stream
122  oStream.flags (oldFlags);
123 }
124 
125 // ///////// Parsing of Options & Configuration /////////
126 // A helper function to simplify the main part.
127 template<class T> std::ostream& operator<< (std::ostream& os,
128  const std::vector<T>& v) {
129  std::copy (v.begin(), v.end(), std::ostream_iterator<T> (std::cout, " "));
130  return os;
131 }
132 
136 int readConfiguration (int argc, char* argv[], bool& ioIsBuiltin,
137  stdair::RandomSeed_T& ioRandomSeed,
138  NbOfRuns_T& ioRandomRuns,
139  stdair::Filename_T& ioInputFilename,
140  stdair::Filename_T& ioOutputFilename,
141  stdair::Filename_T& ioLogFilename,
142  stdair::DemandGenerationMethod& ioDemandGenerationMethod) {
143 
144  // Demand generation method as a single char (e.g., 'P' or 'S').
145  char lDemandGenerationMethodChar;
146 
147  // Default for the built-in input
149 
150  // Declare a group of options that will be allowed only on command line
151  boost::program_options::options_description generic ("Generic options");
152  generic.add_options()
153  ("prefix", "print installation prefix")
154  ("version,v", "print version string")
155  ("help,h", "produce help message");
156 
157  // Declare a group of options that will be allowed both on command
158  // line and in config file
159  boost::program_options::options_description config ("Configuration");
160  config.add_options()
161  ("builtin,b",
162  "The sample BOM tree can be either built-in or parsed from an input file. That latter must then be given with the -i/--input option")
163  ("seed,s",
164  boost::program_options::value<stdair::RandomSeed_T>(&ioRandomSeed)->default_value(K_TRADEMGEN_DEFAULT_RANDOM_SEED),
165  "Seed for the random generation")
166  ("draws,d",
167  boost::program_options::value<NbOfRuns_T>(&ioRandomRuns)->default_value(K_TRADEMGEN_DEFAULT_RANDOM_DRAWS),
168  "Number of runs for the demand generations")
169  ("demandgeneration,G",
170  boost::program_options::value< char >(&lDemandGenerationMethodChar)->default_value(K_TRADEMGEN_DEFAULT_DEMAND_GENERATION_METHOD_CHAR),
171  "Method used to generate the demand (i.e., the booking requests): Poisson Process (P) or Order Statistics (S)")
172  ("input,i",
173  boost::program_options::value< std::string >(&ioInputFilename)->default_value(K_TRADEMGEN_DEFAULT_INPUT_FILENAME),
174  "(CSV) input file for the demand distributions")
175  ("output,o",
176  boost::program_options::value< std::string >(&ioOutputFilename)->default_value(K_TRADEMGEN_DEFAULT_OUTPUT_FILENAME),
177  "(CSV) output file for the generated requests")
178  ("log,l",
179  boost::program_options::value< std::string >(&ioLogFilename)->default_value(K_TRADEMGEN_DEFAULT_LOG_FILENAME),
180  "Filepath for the logs")
181  ;
182 
183  // Hidden options, will be allowed both on command line and
184  // in config file, but will not be shown to the user.
185  boost::program_options::options_description hidden ("Hidden options");
186  hidden.add_options()
187  ("copyright",
188  boost::program_options::value< std::vector<std::string> >(),
189  "Show the copyright (license)");
190 
191  boost::program_options::options_description cmdline_options;
192  cmdline_options.add(generic).add(config).add(hidden);
193 
194  boost::program_options::options_description config_file_options;
195  config_file_options.add(config).add(hidden);
196 
197  boost::program_options::options_description visible ("Allowed options");
198  visible.add(generic).add(config);
199 
200  boost::program_options::positional_options_description p;
201  p.add ("copyright", -1);
202 
203  boost::program_options::variables_map vm;
204  boost::program_options::
205  store (boost::program_options::command_line_parser (argc, argv).
206  options (cmdline_options).positional(p).run(), vm);
207 
208  std::ifstream ifs ("trademgen.cfg");
209  boost::program_options::store (parse_config_file (ifs, config_file_options),
210  vm);
211  boost::program_options::notify (vm);
212 
213  if (vm.count ("help")) {
214  std::cout << visible << std::endl;
216  }
217 
218  if (vm.count ("version")) {
219  std::cout << PACKAGE_NAME << ", version " << PACKAGE_VERSION << std::endl;
221  }
222 
223  if (vm.count ("prefix")) {
224  std::cout << "Installation prefix: " << PREFIXDIR << std::endl;
226  }
227 
228  if (vm.count ("builtin")) {
229  ioIsBuiltin = true;
230  }
231  const std::string isBuiltinStr = (ioIsBuiltin == true)?"yes":"no";
232  std::cout << "The BOM should be built-in? " << isBuiltinStr << std::endl;
233 
234  if (ioIsBuiltin == false) {
235 
236  // The BOM tree should be built from parsing a demand input file
237  if (vm.count ("input")) {
238  ioInputFilename = vm["input"].as< std::string >();
239  std::cout << "Input filename is: " << ioInputFilename << std::endl;
240 
241  } else {
242  // The built-in option is not selected. However, no demand input file
243  // is specified
244  std::cerr << "Either one among the -b/--builtin and -i/--input "
245  << "options must be specified" << std::endl;
246  }
247  }
248 
249  if (vm.count ("output")) {
250  ioOutputFilename = vm["output"].as< std::string >();
251  std::cout << "Output filename is: " << ioOutputFilename << std::endl;
252  }
253 
254  if (vm.count ("log")) {
255  ioLogFilename = vm["log"].as< std::string >();
256  std::cout << "Log filename is: " << ioLogFilename << std::endl;
257  }
258 
259  if (vm.count ("demandgeneration")) {
260  ioDemandGenerationMethod =
261  stdair::DemandGenerationMethod (lDemandGenerationMethodChar);
262  std::cout << "Date-time request generation method is: "
263  << ioDemandGenerationMethod.describe() << std::endl;
264  }
265 
266  //
267  std::cout << "The random generation seed is: " << ioRandomSeed << std::endl;
268 
269  //
270  std::cout << "The number of runs is: " << ioRandomRuns << std::endl;
271 
272  return 0;
273 }
274 
275 // /////////////////////////////////////////////////////////////////////////
277  const stdair::Filename_T& iOutputFilename,
278  const NbOfRuns_T& iNbOfRuns,
279  const stdair::DemandGenerationMethod& iDemandGenerationMethod) {
280 
281  // Open and clean the .csv output file
282  std::ofstream output;
283  output.open (iOutputFilename.c_str());
284  output.clear();
285 
286  // Initialise the statistics collector/accumulator
287  stat_acc_type lStatAccumulator;
288 
289  // Retrieve the expected (mean value of the) number of events to be
290  // generated
291  const stdair::Count_T& lExpectedNbOfEventsToBeGenerated =
293 
294  // Initialise the (Boost) progress display object
295  boost::progress_display lProgressDisplay (lExpectedNbOfEventsToBeGenerated
296  * iNbOfRuns);
297 
298  for (NbOfRuns_T runIdx = 1; runIdx <= iNbOfRuns; ++runIdx) {
299  // /////////////////////////////////////////////////////
300  output << "Run number: " << runIdx << std::endl;
301 
306  const stdair::Count_T& lActualNbOfEventsToBeGenerated =
307  ioTrademgenService.generateFirstRequests (iDemandGenerationMethod);
308 
309  // DEBUG
310  STDAIR_LOG_DEBUG ("[" << runIdx << "] Expected: "
311  << lExpectedNbOfEventsToBeGenerated << ", actual: "
312  << lActualNbOfEventsToBeGenerated);
313 
321  while (ioTrademgenService.isQueueDone() == false) {
322 
323  // Extract the next event from the event queue
324  stdair::EventStruct lEventStruct;
325  stdair::ProgressStatusSet lProgressStatusSet =
326  ioTrademgenService.popEvent (lEventStruct);
327 
328  // DEBUG
329  // STDAIR_LOG_DEBUG ("[" << runIdx << "] Poped event: '"
330  // << lEventStruct.describe() << "'.");
331 
332  // Extract the corresponding demand/booking request
333  const stdair::BookingRequestStruct& lPoppedRequest =
334  lEventStruct.getBookingRequest();
335 
336  // DEBUG
337  STDAIR_LOG_DEBUG ("[" << runIdx << "] Poped booking request: '"
338  << lPoppedRequest.describe() << "'.");
339 
340  // Dump the request into the dedicated CSV file
341  // stdair::BomDisplay::csvDisplay (output, lPoppedRequest);
342 
343  // Retrieve the corresponding demand stream key
344  const stdair::DemandGeneratorKey_T& lDemandStreamKey =
345  lPoppedRequest.getDemandGeneratorKey();
346 
347  // Assess whether more events should be generated for that demand stream
348  const bool stillHavingRequestsToBeGenerated = ioTrademgenService.
349  stillHavingRequestsToBeGenerated (lDemandStreamKey,
350  lProgressStatusSet,
351  iDemandGenerationMethod);
352 
353  // DEBUG
354  STDAIR_LOG_DEBUG (lProgressStatusSet.describe());
355  STDAIR_LOG_DEBUG ("=> [" << lDemandStreamKey << "] is now processed. "
356  << "Still generate events for that demand stream? "
357  << stillHavingRequestsToBeGenerated);
358 
359  // If there are still events to be generated for that demand stream,
360  // generate and add them to the event queue
361  if (stillHavingRequestsToBeGenerated == true) {
362 
363  stdair::BookingRequestPtr_T lNextRequest_ptr =
364  ioTrademgenService.generateNextRequest (lDemandStreamKey,
365  iDemandGenerationMethod);
366 
367  assert (lNextRequest_ptr != NULL);
368 
369  // Sanity check
370  const stdair::Duration_T lDuration =
371  lNextRequest_ptr->getRequestDateTime()
372  - lPoppedRequest.getRequestDateTime();
373  if (lDuration.total_milliseconds() < 0) {
374  STDAIR_LOG_ERROR ("[" << lDemandStreamKey
375  << "] The date-time of the generated event ("
376  << lNextRequest_ptr->getRequestDateTime()
377  << ") is lower than the date-time "
378  << "of the current event ("
379  << lPoppedRequest.getRequestDateTime() << ")");
380  assert (false);
381  }
382 
383  // DEBUG
384  STDAIR_LOG_DEBUG ("[" << lDemandStreamKey << "] Added request: '"
385  << lNextRequest_ptr->describe()
386  << "'. Is queue done? "
387  << ioTrademgenService.isQueueDone());
388  }
389  // DEBUG
390  STDAIR_LOG_DEBUG ("");
391 
392  // Update the progress display
393  ++lProgressDisplay;
394  }
395 
396  // Add the number of events to the statistics accumulator
397  lStatAccumulator (lActualNbOfEventsToBeGenerated);
398 
399  // Reset the service (including the event queue) for the next run
400  ioTrademgenService.reset();
401  }
402 
403  // DEBUG
404  STDAIR_LOG_DEBUG ("End of the demand generation. Following are some "
405  "statistics for the " << iNbOfRuns << " runs.");
406  std::ostringstream oStatStr;
407  stat_display (oStatStr, lStatAccumulator);
408  STDAIR_LOG_DEBUG (oStatStr.str());
409 
410  // DEBUG
411  const std::string& lBOMStr = ioTrademgenService.csvDisplay();
412  STDAIR_LOG_DEBUG (lBOMStr);
413 
414  // Close the output file
415  output.close();
416 }
417 
418 
419 // /////////////// M A I N /////////////////
420 int main (int argc, char* argv[]) {
421 
422  // State whether the BOM tree should be built-in or parsed from an input file
423  bool isBuiltin;
424 
425  // Random generation seed
426  stdair::RandomSeed_T lRandomSeed;
427 
428  // Number of random draws to be generated (best if greater than 100)
429  NbOfRuns_T lNbOfRuns;
430 
431  // Input file name
432  stdair::Filename_T lInputFilename;
433 
434  // Output file name
435  stdair::Filename_T lOutputFilename;
436 
437  // Output log File
438  stdair::Filename_T lLogFilename;
439 
440  // Demand generation method.
441  stdair::DemandGenerationMethod
442  lDemandGenerationMethod (K_TRADEMGEN_DEFAULT_DEMAND_GENERATION_METHOD);
443 
444  // Call the command-line option parser
445  const int lOptionParserStatus =
446  readConfiguration (argc, argv, isBuiltin, lRandomSeed, lNbOfRuns,
447  lInputFilename, lOutputFilename, lLogFilename,
448  lDemandGenerationMethod);
449 
450  if (lOptionParserStatus == K_TRADEMGEN_EARLY_RETURN_STATUS) {
451  return 0;
452  }
453 
454  // Set the log parameters
455  std::ofstream logOutputFile;
456  // Open and clean the log outputfile
457  logOutputFile.open (lLogFilename.c_str());
458  logOutputFile.clear();
459 
460  // Set up the log parameters
461  const stdair::BasLogParams lLogParams (stdair::LOG::DEBUG, logOutputFile);
462 
463  // Initialise the TraDemGen service object
464  TRADEMGEN::TRADEMGEN_Service trademgenService (lLogParams, lRandomSeed);
465 
466  // Check wether or not a (CSV) input file should be read
467  if (isBuiltin == true) {
468  // Create a sample DemandStream object, and insert it within the BOM tree
469  trademgenService.buildSampleBom();
470 
471  } else {
472  // Create the DemandStream objects, and insert them within the BOM tree
473  const TRADEMGEN::DemandFilePath lDemandFilePath (lInputFilename);
474  trademgenService.parseAndLoad (lDemandFilePath);
475  }
476 
477  // Calculate the expected number of events to be generated.
478  generateDemand (trademgenService, lOutputFilename, lNbOfRuns,
479  lDemandGenerationMethod);
480 
481  // Close the Log outputFile
482  logOutputFile.close();
483 
484  /*
485  \note: as that program is not intended to be run on a server in
486  production, it is better not to catch the exceptions. When it
487  happens (that an exception is throwned), that way we get the
488  call stack.
489  */
490 
491  return 0;
492 }