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