//////////////////////////////////////////////////////////////////// /// \file rat.cc /// /// \brief Main /// /// \author Stan Seibert /// /// REVISION HISTORY:\n /// 29 Mar 2010 : Gabriel Orebi Gann - add ``UnPackEvents'' producer /// 19 Dec 2011 : A Mastbaum - add InDispatchProducer /// 28 Sep 2012 : A Mastbaum - remove InDispatchProducer /// 25 Sep 2013 : A Mastbaum - remove InPackedProducer /// 13 May 2015 : W. Heintzelman - add global pointer to the RAT RunManager /// 22 Aug 2015 : N. Barros - Changed default option for -N to -1 /// 22 Nov 2016 : N. Barros - Added run mc mode. /// 04 Dec 2016 : N. Barros - Added list of run-level tables to not use default plane /// 26 Jan 2017 : N. Barros - Changed logging logic to have separate log file level /// 22 Mar 2017 : N. Barros - Added support for default plane lockout and cleared DB initialization /// 26 Oct 2017 : T Kaptanoglu - Add flag to allow working in complete offline mode when processing data /// 11 Jan 2018 : E Leming - Added option to load ratdb server adderss from an environment variable /// 02 Mar 2018 : B. Land - Add flag to set database tag /// 11 Nov 2018 : J. Caravaca - Removed 'cout << cerr' that prevented compilation /// 05 Apr 2020 : D. Auty - Add flag to force save of possible corrupted ZDAB file /// /// \details /// //////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include #include #include #include #include // time.h must come after Randomize.hh on RedHat 7.3 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //For uname() // event producers #include #ifdef COMPILE_DISPATCH #include #endif #include #include #include #include using std::string; using std::vector; using std::cout; using std::endl; using std::cerr; using std::ifstream; using namespace RAT; // Global variable -- pointer to the RAT RunManager: RAT::RunManager* gTheRatRunManager = NULL; class CmdOptions { public: CmdOptions() : seed(-1),save_macro(true), fNumEvents(-1), output_filename(""),run_number(-1), subrun_number(-1), dbserver(""), run_mc_mode(false),no_remote_db(false), default_plane_lock(true), nhits_cut(-1), force_save(false) { }; long seed; bool save_macro; long fNumEvents; std::vector input_filename; std::string output_filename; std::vector python_processors; std::string run_duration; long run_number; long subrun_number; std::string dbserver; std::string ratdb_tag; bool run_mc_mode; bool no_remote_db; bool default_plane_lock; long nhits_cut; bool force_save; }; CmdOptions parse_command_line(int argc, char** argv); void help(); string get_short_hostname(); string get_long_hostname(); void parse_run(std::string str, int &run, int &subrun) { cout << BBLUE << "Overriding run number with " << str << CLR << endl; // First tokenize the string int tmp_num; size_t sep_pos = str.find(':'); std::string tmpstr; if ( sep_pos == std::string::npos) { // There is no subrun if (str.size() > 0) { tmp_num = atol(str.c_str()); if (tmp_num < 1) { cout << BRED << "ERROR:Invalid run number (" << str << ")" << CLR << endl; exit(0); } else { run = tmp_num; } } else { cout << BRED << "ERROR:Input string does not seem to be a valid number (" << str << ")" << CLR << endl; exit(0); } } else { // there is a subrun tmpstr = str.substr(0,sep_pos); if (tmpstr.size() > 0) { tmp_num = atol(tmpstr.c_str()); if (tmp_num < 1) { cout << BRED << "ERROR:Invalid run number (" << tmpstr << ")" << CLR << endl; exit(0); } else { run = tmp_num; } } else { cout << BRED << "ERROR:Input string does not seem to be a valid number (" << str << ")" << CLR << endl; exit(0); } // Reassign for the subrun tmpstr = str.substr(sep_pos+1); if (tmpstr.size() > 0) { tmp_num = atol(tmpstr.c_str()); if (tmp_num < 0) { cout << BRED << "ERROR:Invalid subrun number (" << tmpstr << ")" << CLR << endl; exit(0); } else { subrun = tmp_num; } } else { cout << BRED << "ERROR:Input string does not seem to be a valid number (" << str << ")" << CLR << endl; exit(0); } } } int main (int argc, char** argv) { gRandom = new RAT::Random(); TStopwatch runTime; runTime.Start(); CmdOptions options = parse_command_line(argc, argv); info << RAT::RED << "This is SNO+ RAT " << RAT::GetRATVersion() << " (" << RAT::GetRATRevision() << ")" << RAT::CLR << newline; //Hostname and machine probing. struct utsname nameinfo; int uname_result = uname(&nameinfo); //uname() returns a non-negative integer on success. if ( uname_result >= 0 ) { detail << "Hostname detected: " << nameinfo.nodename << newline; detail << "Hardware detected: " << nameinfo.machine << newline; } //Otherwise notify the user that there was an issue. else { info << "There was a problem detecting machine information. Error strings will" << " be written to the data structure!" << newline; } info << "Status messages enabled: info "; detail << "detail "; debug << "debug "; info << "\n"; // Latch G4 output into RAT::Log streams SetG4coutStream(G4Stream::DETAIL); SetG4cerrStream(G4Stream::WARN); // Seed random number generator using current time and process id // (Use both just in case multiple processes started at same time) time_t start_time = time(NULL); pid_t pid = getpid(); info << "Hostname: " << get_long_hostname() << " PID: " << pid << newline; if (options.seed == -1) // No override by command switch options.seed = start_time ^ (pid << 16); info << BBLUE << "Seeding random number generator: " << CLR << options.seed << newline; CLHEP::HepRandom::setTheSeed(options.seed); // initialize RATDB and load all .ratdb files in $GLG4DATA, // usually set to $RATROOT/data DB *rdb = NULL; DBMessenger *rdb_messenger = NULL; try{ rdb = DB::Get(); if (options.no_remote_db) { info << BCYAN << "Enabling 'airplane mode' (no remote DB access)." << CLR << newline; rdb->SetAirplaneModeStatus(true); } if (!options.default_plane_lock) { info << BCYAN << "Disabling default table lock entirely (BE CAREFUL)." << CLR << newline; rdb->SetDefaultPlaneLockStatus(false); } if (options.run_number != -1) { // -- If a run number is specified, one should block default plane tables rdb->SetRunID(options.run_number); } // If the user explicitly passed a server address, use that if (options.dbserver.length() != 0) { info << BBLUE << "Loading database tables from remote RATDB..." << CLR << newline; rdb->SetServer(options.dbserver,false); } info << BBLUE << "Loading default database tables..." << CLR << newline; rdb->LoadDefaults(); if (options.ratdb_tag.length() != 0) { info << BBLUE << "Using RATDB tag " << options.ratdb_tag << CLR << newline; rdb->SetDbTag(options.ratdb_tag); } if (options.run_number != -1) { rdb->LoadDefaultPlaneLocks(); } rdb_messenger = new DBMessenger(); // Set default input and output files if (options.input_filename.size() != 0) { // This is always executed before the processing of the macros // So it is worth to simply replace rdb->SetSArray("IO", "", "default_input_filename", options.input_filename); info << "Setting default input files to :" << newline; for (size_t i = 0; i < options.input_filename.size(); ++i) { info << " :: " << options.input_filename.at(i) << "\n"; } } if (options.output_filename != "") { rdb->SetS("IO", "", "default_output_filename", options.output_filename); info << "Setting default output file to " << options.output_filename << "\n"; } if( options.fNumEvents != -1 ) { rdb->SetI( "IO", "", "default_num_events", options.fNumEvents ); info << "Setting default number of events to " << options.fNumEvents << "\n"; } if(options.run_duration.size()) { if (options.fNumEvents != -1) { Log::Die("Can't specify both duration and number of events. Choose one.",1); } // Replace the ':' by a space std::replace(options.run_duration.begin(),options.run_duration.end(),':',' '); info << "Setting run to be time controlled with a duration of [" << options.run_duration << "]." << newline; rdb->SetI("IO","","timed_run",1); rdb->SetS("IO","timed_duration",options.run_duration); } if(options.nhits_cut != -1){ if(options.nhits_cut >= 0){ rdb->SetI("NHITS_CUT", "", "nhits_cut", options.nhits_cut); info << "Setting nhits cut to: " << options.nhits_cut << "\n"; } else{ Log::Die("Cannot set nhits cut to negative value."); } } } catch (DBNotFoundError &e) { cerr << "DB: Field " + e.table + "[" + e.index + "]." + e.field + " lookup failure. Does not exist or has wrong type.\n"; if (rdb->GetTblNoDefaultPlaneStatus(e.table,e.index)) { cerr << "rat:: Table " << e.table << "[" << e.index << "] matches a default plane lock entry." << newline; cerr << "rat:: Make sure that the table exists for this run in the remote database." << newline; } return 1; } catch (ParseError& e) { cerr << e.GetFull() << newline; return 1; } catch (int &code) { cerr << "DB: Failed to load the database. Exception caught with code " << code << newline; return code; } catch(std::exception &e) { cerr << "Caught STD exception initializing RATDB : " << e.what() << newline; return 1; } catch(std::string &msg) { cerr << "Caught string exception initializing RATDB : " << msg << newline; return 1; } catch(...) { cerr << "DB: Unknown exception caught." << newline; return 1; } try { // Catch database errors // Main analysis block -- will contain user-constructed analysis sequence info << BBLUE << "Initializing Processor block..." << CLR << newline; ProcBlock *mainBlock = new ProcBlock; // Process block manager -- Supplies user commands to construct analysis // sequence and actually does the processor creation ProcBlockManager *blockManager = new ProcBlockManager(mainBlock); // Build event producers info << BBLUE << "Initializing run manager..." << CLR << newline; RunManager* runManager = new RunManager(mainBlock); if (options.run_number != -1 || options.subrun_number != -1) { warn << BGREEN << "Overriding the (sub)run number to be simulated." << CLR << newline; runManager->GetGsim()->SetRunNumberOverride(options.run_number); runManager->GetGsim()->SetSubrunNumberOverride(options.subrun_number); } if (options.run_mc_mode) { // Run duration and number of events cannot be set when in run_mc mode if (options.run_duration.size()) { Log::Die("Can't set run_mc mode and set a run duration.",1); } if (options.fNumEvents != -1) { Log::Die("Can't set run_mc mode and a number of events.",1); } // the run number has to be specified in run MC mode if (options.run_number == -1) { Log::Die("Run mc mode enabled, but run number not specified.",1); } warn << BGREEN << "** Running in run simulation mode (run_mc) **" << CLR << newline; runManager->GetGsim()->SetRunSimMode(true); } gTheRatRunManager = runManager; info << BBLUE << "Initializing event producers..." << CLR << newline; InROOTProducer* inroot = new InROOTProducer(mainBlock); InZDABProducer* inzdab = new InZDABProducer(mainBlock); if(options.force_save == false){ inzdab->fForceSave = false; } #ifdef COMPILE_DISPATCH InDispatchProducer* indispatch = new InDispatchProducer(mainBlock); #endif InNetProducer* innet = new InNetProducer(mainBlock); InSOCProducer* insoc = new InSOCProducer(mainBlock); // Setup signal handler to intercept Ctrl-C and quit event loop // nicely (closing files and all that). SignalHandler::Init(); // Initialize the user interface info << BBLUE << "Initializing user interface..." << CLR << newline; G4UImanager* theUI = G4UImanager::GetUIpointer(); // Commands can only be removed once without Geant4 segfaulting, so it's best to do so here theUI->RemoveCommand( theUI->GetTree()->FindPath( "/run/beamOn" ) ); // Replaced with /rat/run/start theUI->RemoveCommand( theUI->GetTree()->FindPath( "/tracking/storeTrajectory" ) ); // Replaced with /rat/tracking/store full if (options.run_mc_mode) { // disallow the duration macro command theUI->RemoveCommand( theUI->GetTree()->FindPath( "/rat/run/duration" ) ); } // Add any python processors specified on the command line for (unsigned i=0; i < options.python_processors.size(); i++) { Processor *p = new PythonProc; p->SetS("class", options.python_processors[i]); mainBlock->DeferAddProcessorCommand(p); } // interactive or batch according to command-line args if (optind - argc == 0) { // Interactive mode // G4UIterminal is a (dumb) terminal. // ..but it can be made smart by adding a "shell" to it info << BBLUE << "Starting interactive session..." << CLR << newline; SetG4coutStream(G4Stream::DEFAULT); G4UIsession* theSession = new G4UIterminal(new G4UItcsh); theSession -> SessionStart(); delete theSession; } else { // Batch mode, with optional user interaction G4String command = "/control/execute "; for (int iarg=optind; iarg SessionStart(); delete theSession; } else { if (options.save_macro) { // Check that file exists, then // Read file contents and log them ifstream macro(fileName); string macroline; if ( ! macro.is_open()){ Log::Die("File " + fileName + " does not exist."); } while (!macro.eof()) { getline(macro, macroline); Log::AddMacro(macroline+"\n"); } } // execute given file info << BBLUE << "Running macro..." << CLR << newline; theUI -> ApplyCommand(command+fileName); } } } // User exit or macros finished, clean up // Hack to close GLG4sim output file if used theUI->ApplyCommand("/event/output_file"); // Report on CPU usage struct rusage usage; getrusage(RUSAGE_SELF, &usage); float usertime = usage.ru_utime.tv_sec + usage.ru_utime.tv_usec / 1e6; float systime = usage.ru_stime.tv_sec + usage.ru_stime.tv_usec / 1e6; info << stlplus::dformat("\nRun time: %8.2f sec\n", runTime.RealTime()); info << stlplus::dformat("CPU usage: user %8.2f sec\tsys %6.2f sec\n", usertime, systime); // Check whether the DB fields the user set were actually used rdb->CheckSetFieldUsage(); delete blockManager; delete mainBlock; // implicitly deletes all processor instances the user // built, and closes up associated files delete runManager; delete inroot; delete inzdab; #ifdef COMPILE_DISPATCH delete indispatch; #endif delete innet; delete insoc; delete rdb_messenger; } catch (DBNotFoundError &e) { cerr << "DB: Field " + e.table + "[" + e.index + "]." + e.field + " lookup failure. Does not exist or has wrong type.\n"; return 1; } catch (DS::DataNotFound &e) { cerr << "RAT: RAT::DS::DataNotFound Exception caught accessing a DS variable.\n"; cerr << "Class : " << e.fClassName << endl; cerr << "Field : " << e.fFieldName << endl; cerr << "Detail : " << e.what() << endl; return 1; } catch (ParseError& e) { cerr << e.GetFull() << newline; return 1; } catch (int &code) { cerr << "rat: Caught a return code." << endl; return code; } /** catch logic error exceptions. * This includes invalid_argument, domain_error, length_error, out_of_range */ catch (std::logic_error &e) { cerr << "rat: Logic error caught" << endl; cerr << "details: " << e.what() << endl; return 1; } /** * Catch run time errors: range_error,overflow,underflow, etc */ catch (std::runtime_error &e) { cerr << "rat: Runtime error caught" << endl; cerr << "details: " << e.what() << endl; return 1; } /** * bad_alloc */ catch (std::bad_alloc &e) { cerr << "rat: Bad allocation error caught" << endl; cerr << "details: " << e.what() << endl; return 1; } /** * Generic exception */ catch (std::exception &e) { cerr << "rat: STL exception caught." << endl; cerr << e.what() << endl; return 1; } catch(...) { cerr << "rat: Unidentified exception caught " << std::endl; return 1; } return 0; } #define OPT_NO_SAVE_MACRO 1000 CmdOptions parse_command_line(int argc, char** argv) { static struct option opts[] = { {"quiet", 0, NULL, 'q'}, {"help", 0, NULL, 'h'}, {"verbose", 0, NULL, 'v'}, {"debug", 0, NULL, 'd'}, {"version", 0, NULL, 'V'}, {"logfile", 1, NULL, 'l'}, {"seed", 1, NULL, 's'}, {"input", 1, NULL, 'i'}, {"output", 1, NULL, 'o'}, {"python", 1, NULL, 'p'}, {"no-save-macro", 0, NULL, OPT_NO_SAVE_MACRO}, {"database", 1, NULL, 'b'}, {"run_num", 1, NULL, 'n'}, {"num-events", 1, NULL, 'N'}, {"duration",1,NULL,'T'}, {"run_mc",1,NULL,'r'}, {"log-level",1,NULL,'L'}, {"airplane-mode",0,NULL,'P'}, {"allowdefault",0,NULL,'X'}, {"ratdb_tag",1,NULL,'d'}, {"nhits_cut",1,NULL,'c'}, {"force_save", 0, NULL,'f'}, {0, 0, 0, 0} }; CmdOptions options; int display_level = Log::INFO; int log_level = Log::DETAIL; std::string logfilename = std::string("rat.")+get_short_hostname()+"."+std::to_string(getpid())+".log"; std::string strrun; int run = -1, subrun = -1; int option_index = 0; int c = getopt_long(argc, argv, "qhvVPXfl:n:r:s:b:d:o:i:p:N:T:L:c:", opts, &option_index); while (c != -1) { switch (c) { case 'q': if (display_level > Log::WARN) display_level--; break; case 'v': if (display_level < Log::DEBUG) display_level++; break; case 'h': help(); exit(0); break; case 'V': cout << "SNO+ RAT " << RAT::GetRATVersion() << " (" << RAT::GetRATRevision() << ")" << std::endl; exit(0); break; case 'n': strrun = optarg; parse_run(strrun,run,subrun); options.run_number = run; options.subrun_number = subrun; break; case 'l': logfilename = optarg; break; case 'L': log_level = atol(optarg); break; case 's': options.seed = atol(optarg); break; case 'b': options.dbserver = optarg; break; case 'd': options.ratdb_tag = optarg; break; case 'i': options.input_filename.push_back(optarg); break; case 'o': options.output_filename = optarg; break; case 'p': options.python_processors.push_back(optarg); break; case 'N': options.fNumEvents = atol(optarg); break; case 'c': options.nhits_cut = atol(optarg); break; case 'T': options.run_duration = optarg; break; case 'r': options.run_mc_mode = true; strrun = optarg; parse_run(strrun,run,subrun); options.run_number = run; options.subrun_number = subrun; break; case 'P': options.no_remote_db = true; break; case 'X': options.default_plane_lock = false; break; case OPT_NO_SAVE_MACRO: options.save_macro = false; break; case 'f': options.force_save = true; break; default: exit(1); } c = getopt_long(argc, argv, "qhvVPXfl:n:r:s:b:d:o:i:p:N:T:L:c:", opts, &option_index); } // Setup logging if (log_level > 3) { cout << "Warning: Invalid file log level [" << log_level << "]. Setting it to 'debug' (3)" << endl; log_level = 3; } else if (log_level < 0) { cout << "Warning: Invalid file log level [" << log_level << "]. Setting it to 'warn' (0)" << endl; log_level = 0; } Log::Init(logfilename, Log::Level(display_level), Log::Level(log_level)); return options; } void help(){ // Logging not yet initialized so use cout cout << "SNO+ RAT " << RAT::GetRATVersion() << " (" << RAT::GetRATRevision() << ")" << std::endl << std::endl; cout << "Usage: rat [flags] macro1.mac macro2.mac ...\n"; cout << " -q, --quiet Quiet mode, only show warnings\n"; cout << " -v, --verbose Verbose mode, show more information.\n" " Multiple -v options increase verbosity. " "The maximum is 2\n"; cout << " -L, --log-level Level of file logging (0:warn,1:info,2:detail[default],3:debug)\n"; cout << " -V, --version Show program version and exit\n"; cout << " -h, --help Display this help message and exit\n"; cout << " -l, --log Set log filename. " "Defaults to rat.[host].[pid].log\n"; cout << " -n, --run_num Set the run number. Defaults to MC RATDB entry (0).\n"; cout << " -s, --seed Set random number seed.\n" " If no -s option is present, default is to seed with\n" " XOR of clock time and process id.\n"; cout << " -i, --input Set default input filename. \n" " (Does not override filename in macro!)\n"; cout << " -o, --output Set default output filename. \n" " (Does not override filename in macro!)\n"; cout << " -p, --python=class Append python processor to event loop\n"; cout << " -N, --num-events Set the number of events to simulate\n" " or the total number of events processed for data\n"; cout << " -c, --nhits_cut Set the value of the nhits cut in the nhit cut proc.\n"; cout << " -T, --duration Set the time span of the simulation. \n" " (Exclusive with number of events.)\n"; cout << " -b, --database URL to PgSQL database\n"; cout << " -d, --ratdb_tag RATDB database tag to use; live view if unspecified.\n"; cout << " -r, --run_mc Set the execution to be in run simulation mode.\n"; cout << " In this mode, RAT will use the run number (passed as argument) and the information\n"; cout << " loaded from the RUN ratdb table to set the duration of the simulation.\n"; cout << " In this mode, one cannot set neither the duration nor the number of events.\n"; cout << " --no-save-macro Do not save macro into output ROOT file.\n"; cout << " -P, --airplane-mode Activate airplane mode, i.e., do not connect to the remote database.\n"; cout << " -X, --allowdefault Allow default plane access when processing data. Use with -P for complete offline mode.\n" " WARNING: Use extreme care working in complete offline mode.\n"; cout<< " -f, --force_save Save the output even when the file is corrupted.\n"; } std::string get_short_hostname() { char c_hostname[256]; gethostname(c_hostname, 256); std::string hostname(c_hostname); std::vector parts = util_split(hostname, "."); if (parts.size() > 0) return parts[0]; else return std::string("unknown"); } std::string get_long_hostname() { char c_hostname[256]; gethostname(c_hostname, 256); return std::string(c_hostname); }