TraDemGen Logo  0.2.2
C++ Simulated Travel Demand Generation Library
 All Classes Namespaces Files Functions Variables Typedefs Friends Defines
trademgen.cpp
Go to the documentation of this file.
00001 // //////////////////////////////////////////////////////////////////////
00002 // Import section
00003 // //////////////////////////////////////////////////////////////////////
00004 // STL
00005 #include <cassert>
00006 #include <sstream>
00007 #include <fstream>
00008 #include <vector>
00009 #include <list>
00010 #include <string>
00011 //  //// Boost (Extended STL) ////
00012 // Boost Tokeniser
00013 #include <boost/tokenizer.hpp>
00014 // Boost Program Options
00015 #include <boost/program_options.hpp>
00016 // Boost Accumulators
00017 #include <boost/accumulators/accumulators.hpp>
00018 #include <boost/accumulators/statistics.hpp>
00019 // Boost Progress
00020 //#include <boost/progress.hpp>
00021 // StdAir
00022 #include <stdair/stdair_basic_types.hpp>
00023 #include <stdair/basic/BasConst_General.hpp>
00024 #include <stdair/basic/ProgressStatusSet.hpp>
00025 #include <stdair/basic/DemandGenerationMethod.hpp>
00026 #include <stdair/bom/EventStruct.hpp>
00027 #include <stdair/bom/EventQueue.hpp>
00028 #include <stdair/bom/BookingRequestStruct.hpp>
00029 #include <stdair/bom/BomDisplay.hpp>
00030 #include <stdair/service/Logger.hpp>
00031 // TraDemGen
00032 #include <trademgen/TRADEMGEN_Service.hpp>
00033 #include <trademgen/config/trademgen-paths.hpp>
00034 #include <trademgen/config/trademgen-paths.hpp>
00035 
00036 // Aliases for namespaces
00037 namespace ba = boost::accumulators;
00038 
00039 // //////// Specific type definitions ///////
00040 typedef unsigned int NbOfRuns_T;
00041 
00045 typedef ba::accumulator_set<double,
00046                             ba::stats<ba::tag::min, ba::tag::max,
00047                                       ba::tag::mean (ba::immediate),
00048                                       ba::tag::sum,
00049                                       ba::tag::variance> > stat_acc_type;
00050 
00051 // //////// Constants //////
00055 const stdair::Filename_T K_TRADEMGEN_DEFAULT_LOG_FILENAME ("trademgen.log");
00056 
00060 const stdair::Filename_T K_TRADEMGEN_DEFAULT_INPUT_FILENAME (STDAIR_SAMPLE_DIR
00061                                                              "/demand01.csv");
00062 
00066 const stdair::Filename_T K_TRADEMGEN_DEFAULT_OUTPUT_FILENAME ("request.csv");
00067 
00071 const stdair::DemandGenerationMethod
00072 K_TRADEMGEN_DEFAULT_DEMAND_GENERATION_METHOD =
00073   stdair::DemandGenerationMethod::POI_PRO;
00074 
00078 const char K_TRADEMGEN_DEFAULT_DEMAND_GENERATION_METHOD_CHAR =
00079   K_TRADEMGEN_DEFAULT_DEMAND_GENERATION_METHOD.getMethodAsChar();
00080 
00084 const stdair::RandomSeed_T K_TRADEMGEN_DEFAULT_RANDOM_SEED =
00085   stdair::DEFAULT_RANDOM_SEED;
00086 
00090 const NbOfRuns_T K_TRADEMGEN_DEFAULT_RANDOM_DRAWS = 1;
00091 
00096 const bool K_TRADEMGEN_DEFAULT_BUILT_IN_INPUT = false;
00097 
00101 const int K_TRADEMGEN_EARLY_RETURN_STATUS = 99;
00102 
00103 
00107 void stat_display (std::ostream& oStream, const stat_acc_type& iStatAcc) {
00108 
00109   // Store current formatting flags of the output stream
00110   std::ios::fmtflags oldFlags = oStream.flags();
00111 
00112   //
00113   oStream.setf (std::ios::fixed);
00114   
00115   //
00116   oStream << "Statistics for the demand generation runs: " << std::endl;
00117   oStream << "  minimum   = " << ba::min (iStatAcc) << std::endl;
00118   oStream << "  mean      = " << ba::mean (iStatAcc) << std::endl;
00119   oStream << "  maximum   = " << ba::max (iStatAcc) << std::endl;
00120   oStream << "  count     = " << ba::count (iStatAcc) << std::endl;
00121   oStream << "  variance  = " << ba::variance (iStatAcc) << std::endl;
00122   
00123   // Reset formatting flags of output stream
00124   oStream.flags (oldFlags);
00125 }
00126 
00127 // ///////// Parsing of Options & Configuration /////////
00128 // A helper function to simplify the main part.
00129 template<class T> std::ostream& operator<< (std::ostream& os,
00130                                             const std::vector<T>& v) {
00131   std::copy (v.begin(), v.end(), std::ostream_iterator<T> (std::cout, " ")); 
00132   return os;
00133 }
00134 
00138 int readConfiguration (int argc, char* argv[], bool& ioIsBuiltin,
00139                        stdair::RandomSeed_T& ioRandomSeed,
00140                        NbOfRuns_T& ioRandomRuns,
00141                        stdair::Filename_T& ioInputFilename,
00142                        stdair::Filename_T& ioOutputFilename,
00143                        stdair::Filename_T& ioLogFilename,
00144                        stdair::DemandGenerationMethod& ioDemandGenerationMethod) {
00145 
00146   // Demand generation method as a single char (e.g., 'P' or 'S').
00147   char lDemandGenerationMethodChar;
00148 
00149   // Default for the built-in input
00150   ioIsBuiltin = K_TRADEMGEN_DEFAULT_BUILT_IN_INPUT;
00151 
00152   // Declare a group of options that will be allowed only on command line
00153   boost::program_options::options_description generic ("Generic options");
00154   generic.add_options()
00155     ("prefix", "print installation prefix")
00156     ("version,v", "print version string")
00157     ("help,h", "produce help message");
00158     
00159   // Declare a group of options that will be allowed both on command
00160   // line and in config file
00161   boost::program_options::options_description config ("Configuration");
00162   config.add_options()
00163     ("builtin,b",
00164      "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")
00165     ("seed,s",
00166      boost::program_options::value<stdair::RandomSeed_T>(&ioRandomSeed)->default_value(K_TRADEMGEN_DEFAULT_RANDOM_SEED),
00167      "Seed for the random generation")
00168     ("draws,d",
00169      boost::program_options::value<NbOfRuns_T>(&ioRandomRuns)->default_value(K_TRADEMGEN_DEFAULT_RANDOM_DRAWS), 
00170      "Number of runs for the demand generations")
00171     ("demandgeneration,G",
00172      boost::program_options::value< char >(&lDemandGenerationMethodChar)->default_value(K_TRADEMGEN_DEFAULT_DEMAND_GENERATION_METHOD_CHAR),
00173      "Method used to generate the demand (i.e., the booking requests): Poisson Process (P) or Order Statistics (S)")
00174     ("input,i",
00175      boost::program_options::value< std::string >(&ioInputFilename)->default_value(K_TRADEMGEN_DEFAULT_INPUT_FILENAME),
00176      "(CSV) input file for the demand distributions")
00177     ("output,o",
00178      boost::program_options::value< std::string >(&ioOutputFilename)->default_value(K_TRADEMGEN_DEFAULT_OUTPUT_FILENAME),
00179      "(CSV) output file for the generated requests")
00180     ("log,l",
00181      boost::program_options::value< std::string >(&ioLogFilename)->default_value(K_TRADEMGEN_DEFAULT_LOG_FILENAME),
00182      "Filepath for the logs")
00183     ;
00184 
00185   // Hidden options, will be allowed both on command line and
00186   // in config file, but will not be shown to the user.
00187   boost::program_options::options_description hidden ("Hidden options");
00188   hidden.add_options()
00189     ("copyright",
00190      boost::program_options::value< std::vector<std::string> >(),
00191      "Show the copyright (license)");
00192         
00193   boost::program_options::options_description cmdline_options;
00194   cmdline_options.add(generic).add(config).add(hidden);
00195 
00196   boost::program_options::options_description config_file_options;
00197   config_file_options.add(config).add(hidden);
00198 
00199   boost::program_options::options_description visible ("Allowed options");
00200   visible.add(generic).add(config);
00201         
00202   boost::program_options::positional_options_description p;
00203   p.add ("copyright", -1);
00204         
00205   boost::program_options::variables_map vm;
00206   boost::program_options::
00207     store (boost::program_options::command_line_parser (argc, argv).
00208            options (cmdline_options).positional(p).run(), vm);
00209 
00210   std::ifstream ifs ("trademgen.cfg");
00211   boost::program_options::store (parse_config_file (ifs, config_file_options),
00212                                  vm);
00213   boost::program_options::notify (vm);
00214     
00215   if (vm.count ("help")) {
00216     std::cout << visible << std::endl;
00217     return K_TRADEMGEN_EARLY_RETURN_STATUS;
00218   }
00219 
00220   if (vm.count ("version")) {
00221     std::cout << PACKAGE_NAME << ", version " << PACKAGE_VERSION << std::endl;
00222     return K_TRADEMGEN_EARLY_RETURN_STATUS;
00223   }
00224 
00225   if (vm.count ("prefix")) {
00226     std::cout << "Installation prefix: " << PREFIXDIR << std::endl;
00227     return K_TRADEMGEN_EARLY_RETURN_STATUS;
00228   }
00229 
00230   if (vm.count ("builtin")) {
00231     ioIsBuiltin = true;
00232   }
00233   const std::string isBuiltinStr = (ioIsBuiltin == true)?"yes":"no";
00234   std::cout << "The BOM should be built-in? " << isBuiltinStr << std::endl;
00235 
00236   if (ioIsBuiltin == false) {
00237 
00238     // The BOM tree should be built from parsing a demand input file
00239     if (vm.count ("input")) {
00240       ioInputFilename = vm["input"].as< std::string >();
00241       std::cout << "Input filename is: " << ioInputFilename << std::endl;
00242 
00243     } else {
00244       // The built-in option is not selected. However, no demand input file
00245       // is specified
00246       std::cerr << "Either one among the -b/--builtin and -i/--input "
00247                 << "options must be specified" << std::endl;
00248     }
00249   }
00250 
00251   if (vm.count ("output")) {
00252     ioOutputFilename = vm["output"].as< std::string >();
00253     std::cout << "Output filename is: " << ioOutputFilename << std::endl;
00254   }
00255 
00256   if (vm.count ("log")) {
00257     ioLogFilename = vm["log"].as< std::string >();
00258     std::cout << "Log filename is: " << ioLogFilename << std::endl;
00259   }
00260 
00261   if (vm.count ("demandgeneration")) {
00262     ioDemandGenerationMethod =
00263       stdair::DemandGenerationMethod (lDemandGenerationMethodChar);
00264     std::cout << "Date-time request generation method is: "
00265               << ioDemandGenerationMethod.describe() << std::endl;
00266   }
00267 
00268   //
00269   std::cout << "The random generation seed is: " << ioRandomSeed << std::endl;
00270 
00271   //
00272   std::cout << "The number of runs is: " << ioRandomRuns << std::endl;
00273   
00274   return 0;
00275 }
00276 
00277 // /////////////////////////////////////////////////////////////////////////
00278 void generateDemand (TRADEMGEN::TRADEMGEN_Service& ioTrademgenService,
00279                      const stdair::Filename_T& iOutputFilename,
00280                      const NbOfRuns_T& iNbOfRuns,
00281                      const stdair::DemandGenerationMethod& iDemandGenerationMethod) {
00282 
00283   // Open and clean the .csv output file
00284   std::ofstream output;
00285   output.open (iOutputFilename.c_str());
00286   output.clear();
00287     
00288   // Initialise the statistics collector/accumulator
00289   stat_acc_type lStatAccumulator;
00290 
00291   // Retrieve the expected (mean value of the) number of events to be
00292   // generated
00293   const stdair::Count_T& lExpectedNbOfEventsToBeGenerated =
00294     ioTrademgenService.getExpectedTotalNumberOfRequestsToBeGenerated();
00295 
00296   // Initialise the (Boost) progress display object
00297   boost::progress_display lProgressDisplay (lExpectedNbOfEventsToBeGenerated
00298                                             * iNbOfRuns);
00299   
00300   for (NbOfRuns_T runIdx = 1; runIdx <= iNbOfRuns; ++runIdx) {
00301     // /////////////////////////////////////////////////////
00302     output << "Run number: " << runIdx << std::endl;
00303 
00308     const stdair::Count_T& lActualNbOfEventsToBeGenerated =
00309       ioTrademgenService.generateFirstRequests (iDemandGenerationMethod);
00310 
00311     // DEBUG
00312     STDAIR_LOG_DEBUG ("[" << runIdx << "] Expected: "
00313                       << lExpectedNbOfEventsToBeGenerated << ", actual: "
00314                       << lActualNbOfEventsToBeGenerated);
00315       
00323     while (ioTrademgenService.isQueueDone() == false) {
00324 
00325       // Extract the next event from the event queue
00326       stdair::EventStruct lEventStruct;
00327       stdair::ProgressStatusSet lProgressStatusSet =
00328         ioTrademgenService.popEvent (lEventStruct);
00329       
00330       // DEBUG
00331       // STDAIR_LOG_DEBUG ("[" << runIdx << "] Poped event: '"
00332       //                   << lEventStruct.describe() << "'.");
00333       
00334       // Extract the corresponding demand/booking request
00335       const stdair::BookingRequestStruct& lPoppedRequest =
00336         lEventStruct.getBookingRequest();
00337         
00338       // DEBUG
00339       STDAIR_LOG_DEBUG ("[" << runIdx << "] Poped booking request: '"
00340                         << lPoppedRequest.describe() << "'.");
00341     
00342       // Dump the request into the dedicated CSV file
00343       // stdair::BomDisplay::csvDisplay (output, lPoppedRequest);
00344         
00345       // Retrieve the corresponding demand stream key
00346       const stdair::DemandGeneratorKey_T& lDemandStreamKey =
00347         lPoppedRequest.getDemandGeneratorKey();
00348       
00349       // Assess whether more events should be generated for that demand stream
00350       const bool stillHavingRequestsToBeGenerated = ioTrademgenService.
00351         stillHavingRequestsToBeGenerated (lDemandStreamKey,
00352                                           lProgressStatusSet,
00353                                           iDemandGenerationMethod);
00354 
00355       // DEBUG
00356       STDAIR_LOG_DEBUG (lProgressStatusSet.describe());
00357       STDAIR_LOG_DEBUG ("=> [" << lDemandStreamKey << "] is now processed. "
00358                         << "Still generate events for that demand stream? "
00359                         << stillHavingRequestsToBeGenerated);
00360     
00361       // If there are still events to be generated for that demand stream,
00362       // generate and add them to the event queue
00363       if (stillHavingRequestsToBeGenerated == true) {
00364         
00365         stdair::BookingRequestPtr_T lNextRequest_ptr =
00366           ioTrademgenService.generateNextRequest (lDemandStreamKey,
00367                                                   iDemandGenerationMethod);
00368         
00369         assert (lNextRequest_ptr != NULL);
00370 
00371         // Sanity check
00372         const stdair::Duration_T lDuration =
00373           lNextRequest_ptr->getRequestDateTime()
00374           - lPoppedRequest.getRequestDateTime();
00375         if (lDuration.total_milliseconds() < 0) {
00376           STDAIR_LOG_ERROR ("[" << lDemandStreamKey
00377                             << "] The date-time of the generated event ("
00378                             << lNextRequest_ptr->getRequestDateTime()
00379                             << ") is lower than the date-time "
00380                             << "of the current event ("
00381                             << lPoppedRequest.getRequestDateTime() << ")");
00382           assert (false);
00383         }
00384 
00385         // DEBUG
00386         STDAIR_LOG_DEBUG ("[" << lDemandStreamKey << "] Added request: '"
00387                           << lNextRequest_ptr->describe()
00388                           << "'. Is queue done? "
00389                           << ioTrademgenService.isQueueDone());
00390       }
00391       // DEBUG
00392       STDAIR_LOG_DEBUG ("");
00393       
00394       // Update the progress display
00395       ++lProgressDisplay;
00396     }
00397 
00398     // Add the number of events to the statistics accumulator
00399     lStatAccumulator (lActualNbOfEventsToBeGenerated);
00400     
00401     // Reset the service (including the event queue) for the next run
00402     ioTrademgenService.reset();
00403   }
00404 
00405   // DEBUG
00406   STDAIR_LOG_DEBUG ("End of the demand generation. Following are some "
00407                     "statistics for the " << iNbOfRuns << " runs.");
00408   std::ostringstream oStatStr;
00409   stat_display (oStatStr, lStatAccumulator);
00410   STDAIR_LOG_DEBUG (oStatStr.str());
00411 
00412   // DEBUG
00413   const std::string& lBOMStr = ioTrademgenService.csvDisplay();
00414   STDAIR_LOG_DEBUG (lBOMStr);
00415 
00416   // Close the output file
00417   output.close();
00418 }
00419 
00420 
00421 // /////////////// M A I N /////////////////
00422 int main (int argc, char* argv[]) {
00423 
00424   // State whether the BOM tree should be built-in or parsed from an input file
00425   bool isBuiltin;
00426 
00427   // Random generation seed
00428   stdair::RandomSeed_T lRandomSeed;
00429 
00430   // Number of random draws to be generated (best if greater than 100)
00431   NbOfRuns_T lNbOfRuns;
00432     
00433   // Input file name
00434   stdair::Filename_T lInputFilename;
00435 
00436   // Output file name
00437   stdair::Filename_T lOutputFilename;
00438 
00439   // Output log File
00440   stdair::Filename_T lLogFilename;
00441   
00442   // Demand generation method.
00443   stdair::DemandGenerationMethod
00444     lDemandGenerationMethod (K_TRADEMGEN_DEFAULT_DEMAND_GENERATION_METHOD);
00445 
00446   // Call the command-line option parser
00447   const int lOptionParserStatus = 
00448     readConfiguration (argc, argv, isBuiltin, lRandomSeed, lNbOfRuns,
00449                        lInputFilename, lOutputFilename, lLogFilename,
00450                        lDemandGenerationMethod);
00451 
00452   if (lOptionParserStatus == K_TRADEMGEN_EARLY_RETURN_STATUS) {
00453     return 0;
00454   }
00455 
00456   // Set the log parameters
00457   std::ofstream logOutputFile;
00458   // Open and clean the log outputfile
00459   logOutputFile.open (lLogFilename.c_str());
00460   logOutputFile.clear();
00461 
00462   // Set up the log parameters
00463   const stdair::BasLogParams lLogParams (stdair::LOG::DEBUG, logOutputFile);
00464 
00465   // Initialise the TraDemGen service object
00466   TRADEMGEN::TRADEMGEN_Service trademgenService (lLogParams, lRandomSeed);
00467 
00468   // Check wether or not a (CSV) input file should be read
00469   if (isBuiltin == true) {
00470     // Create a sample DemandStream object, and insert it within the BOM tree
00471     trademgenService.buildSampleBom();
00472 
00473   } else {
00474     // Create the DemandStream objects, and insert them within the BOM tree
00475     trademgenService.parseAndLoad (lInputFilename);
00476   }  
00477 
00478   // Calculate the expected number of events to be generated.
00479   generateDemand (trademgenService, lOutputFilename, lNbOfRuns,
00480                   lDemandGenerationMethod);
00481 
00482   // Close the Log outputFile
00483   logOutputFile.close();
00484 
00485   /*
00486     \note: as that program is not intended to be run on a server in
00487     production, it is better not to catch the exceptions. When it
00488     happens (that an exception is throwned), that way we get the
00489     call stack.
00490   */
00491 
00492   return 0;
00493 }