File indexing completed on 2025-07-05 08:25:49
0001 #ifdef __MACH__
0002 #include <mach/mach.h>
0003 #endif
0004
0005 #include <iostream>
0006 #include <fstream>
0007 #include <string>
0008 #include <random>
0009 #include <ctime>
0010 #include <cstdlib>
0011 #include <vector>
0012 #include <algorithm>
0013 #include <numeric>
0014 #include <stdexcept>
0015 #include <chrono>
0016 #include <cmath>
0017 #include <random>
0018 #include <tuple>
0019 #include <sys/resource.h>
0020
0021 #include <HepMC3/ReaderFactory.h>
0022 #include <HepMC3/WriterAscii.h>
0023 #include "HepMC3/WriterRootTree.h"
0024 #include "HepMC3/GenRunInfo.h"
0025 #include "HepMC3/GenEvent.h"
0026 #include "HepMC3/Print.h"
0027
0028 #include "argparse/argparse.hpp"
0029
0030 using std::cout;
0031 using std::cerr;
0032 using std::endl;
0033 using std::string;
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049 struct BackgroundConfig {
0050 std::string file;
0051 double frequency=0;
0052 int skip=0;
0053 int status=0;
0054 } ;
0055
0056 class SignalBackgroundMerger {
0057
0058 private:
0059
0060 std::shared_ptr<HepMC3::Reader> sigAdapter;
0061 double sigFreq = 0;
0062 int sigStatus = 0;
0063 std::map<std::string, std::shared_ptr<HepMC3::Reader>> freqAdapters;
0064 std::map<std::string, double> freqs;
0065 std::map<std::string, int> baseStatuses;
0066
0067 std::map<std::string,
0068 std::tuple<std::vector<HepMC3::GenEvent>,
0069 std::piecewise_constant_distribution<>,
0070 double>
0071 > weightDict;
0072
0073
0074 typedef struct{
0075 long eventCount;
0076 long particleCount;
0077 } stats;
0078 std::map<std::string, stats > infoDict;
0079
0080 public:
0081
0082 SignalBackgroundMerger(int argc, char* argv[]) {
0083 auto t0 = std::chrono::high_resolution_clock::now();
0084
0085
0086 digestArgs(argc, argv);
0087 rng.seed( rngSeed );
0088 banner();
0089 if (outputFile != "" ) {
0090 outputFileName = outputFile;
0091 } else {
0092 outputFileName = nameGen();
0093 }
0094 std::cout << "\n==================================================================\n";
0095 cout << "Writing to " << outputFileName << endl;
0096
0097 PrepData ( signalFile, signalFreq, signalSkip, signalStatus, true );
0098 for (const auto& bg : backgroundFiles) {
0099 PrepData ( bg.file, bg.frequency, bg.skip, bg.status, false );
0100 }
0101
0102
0103 auto t1 = std::chrono::high_resolution_clock::now();
0104 std::cout << "Initiation time: " << std::round(std::chrono::duration<double, std::chrono::seconds::period>(t1 - t0).count()) << " sec" << std::endl;
0105 std::cout << "\n==================================================================\n" << std::endl;
0106
0107 }
0108
0109
0110 std::vector<BackgroundConfig>
0111 parse_backgrounds(const std::vector<std::string> &raw_args_list) {
0112 std::vector<BackgroundConfig> backgrounds;
0113 auto is_pure_integer = [](const std::string &str) {
0114 if (str.empty()) return false;
0115 for (char c : str) {
0116 if (!std::isdigit(c)) return false;
0117 }
0118 return true;
0119 };
0120
0121
0122 for (size_t i = 0; i < raw_args_list.size();) {
0123
0124 size_t args_count = 2;
0125
0126
0127 if (i + 2 < raw_args_list.size() && is_pure_integer(raw_args_list[i + 2])) {
0128 args_count = 3;
0129
0130 if (i + 3 < raw_args_list.size() && is_pure_integer(raw_args_list[i + 3])) {
0131 args_count = 4;
0132 }
0133 }
0134
0135
0136 if (i + args_count > raw_args_list.size()) {
0137 args_count = raw_args_list.size() - i;
0138 }
0139
0140 if (args_count < 2) {
0141 throw std::runtime_error("Background file " +
0142 std::to_string(backgrounds.size()) +
0143 " must have at least 2 arguments");
0144 }
0145
0146 try {
0147 BackgroundConfig bg;
0148 bg.file = raw_args_list[i];
0149 bg.frequency = std::stod(raw_args_list[i + 1]);
0150 bg.skip = (args_count > 2) ? std::stoi(raw_args_list[i + 2]) : 0;
0151 bg.status = (args_count > 3) ? std::stoi(raw_args_list[i + 3]) : 0;
0152 backgrounds.push_back(bg);
0153 } catch (const std::exception &e) {
0154 throw std::runtime_error("Error parsing background file " +
0155 std::to_string(backgrounds.size()) + ": " +
0156 e.what());
0157 }
0158
0159 i += args_count;
0160 }
0161
0162 return backgrounds;
0163 }
0164
0165 void merge(){
0166 auto t1 = std::chrono::high_resolution_clock::now();
0167
0168
0169 std::shared_ptr<HepMC3::Writer> f;
0170 if (rootFormat)
0171 f = std::make_shared<HepMC3::WriterRootTree>(outputFileName);
0172 else
0173 f = std::make_shared<HepMC3::WriterAscii>(outputFileName);
0174
0175
0176 int i = 0;
0177 for (i = 0; i<nSlices; ++i ) {
0178 if (i % 1000 == 0 || verbose ) squawk(i);
0179 auto hepSlice = mergeSlice(i);
0180 if (!hepSlice) {
0181 std::cout << "Exhausted signal source." << std::endl;
0182 break;
0183 }
0184 hepSlice->set_event_number(i);
0185 f->write_event(*hepSlice);
0186 }
0187 std::cout << "Finished all requested slices." << std::endl;
0188
0189 int slicesDone = i;
0190 auto t2 = std::chrono::high_resolution_clock::now();
0191
0192 std::cout << "Slice loop time: " << std::round(std::chrono::duration<double, std::chrono::minutes::period>(t2 - t1).count()) << " min" << std::endl;
0193 std::cout << " -- " << std::round(std::chrono::duration<double, std::chrono::microseconds::period>(t2 - t1).count() / i) << " us / slice" << std::endl;
0194
0195 for (auto info : infoDict) {
0196 std::cout << "From " << info.first << std::endl;
0197 std::cout << " placed " << info.second.eventCount << " events" << std::endl;
0198 std::cout << " --> on average " << std::setprecision(3) << info.second.eventCount / float(nSlices) << std::endl;
0199 std::cout << " placed " << info.second.particleCount << " final state particles" << std::endl;
0200 std::cout << " --> on average " << std::setprecision(3) << info.second.particleCount / float(nSlices) << std::endl;
0201
0202 }
0203
0204 struct rusage r_usage;
0205 getrusage(RUSAGE_SELF, &r_usage);
0206
0207
0208
0209
0210 #ifdef __MACH__
0211 float mbsize = 1024 * 1024;
0212 #else
0213 float mbsize = 1024;
0214 #endif
0215
0216
0217 std::cout << endl << "Maximum Resident Memory " << r_usage.ru_maxrss / mbsize << " MB" << std::endl;
0218
0219 sigAdapter->close();
0220 for (auto& it : freqAdapters) {
0221 it.second->close();
0222 }
0223 f->close();
0224
0225 }
0226
0227
0228 void digestArgs(int argc, char* argv[]) {
0229
0230
0231
0232
0233
0234 argparse::ArgumentParser args ("Merge signal events with up to four background sources.");
0235
0236 args.add_argument("-i", "--signalFile")
0237 .default_value(std::string("root://dtn-eic.jlab.org//volatile/eic/EPIC/EVGEN/SIDIS/pythia6-eic/1.0.0/10x100/q2_0to1/pythia_ep_noradcor_10x100_q2_0.000000001_1.0_run1.ab.hepmc3.tree.root"))
0238 .help("Name of the HEPMC file with the signal events");
0239
0240 args.add_argument("-sf", "--signalFreq")
0241 .default_value(0.0)
0242 .scan<'g', double>()
0243 .help("Signal frequency in kHz. Default is 0 to have exactly one signal event per slice. Set to the estimated DIS rate to randomize.");
0244
0245 args.add_argument("-S", "--signalSkip")
0246 .default_value(0)
0247 .scan<'i', int>()
0248 .help("Number of signals events to skip. Default is 0.");
0249
0250 args.add_argument("-St", "--signalStatus")
0251 .default_value(0)
0252 .scan<'i', int>()
0253 .help("Apply shift on particle generatorStatus code for signal. Default is 0. ");
0254
0255 args.add_argument("-b","--bgFile")
0256 .nargs(2,4)
0257 .append()
0258 .help("Tuple with name of the HEPMC file with background events, background frequency in kHz, number of background events to skip (default 0), shift on particle generatorStatus code (default 0).");
0259
0260 args.add_argument("-o", "--outputFile")
0261 .default_value(std::string("bgmerged.hepmc3.tree.root"))
0262 .help("Specify the output file name. By default bgmerged.hepmc3.tree.root is used");
0263
0264 args.add_argument("-r", "--rootFormat")
0265 .default_value(true)
0266 .implicit_value(true)
0267 .help("Use hepmc.root output format, default is true.");
0268
0269 args.add_argument("-w", "--intWindow")
0270 .default_value(2000.0)
0271 .scan<'g', double>()
0272 .help("Length of the integration window in nanoseconds. Default is 2000.");
0273
0274 args.add_argument("-N", "--nSlices")
0275 .default_value(10000)
0276 .scan<'i', int>()
0277 .help("Number of sampled time slices ('events'). Default is 10000. If set to -1, all events in the signal file will be used and background files cycled as needed.");
0278
0279 args.add_argument("--squashTime")
0280 .default_value(false)
0281 .implicit_value(true)
0282 .help("Integration is performed but no time information is associated to vertices.");
0283
0284 args.add_argument("--rngSeed")
0285 .default_value(0)
0286 .action([](const std::string& value) { return std::stoi(value); })
0287 .help("Random seed, default is None");
0288
0289 args.add_argument("-v", "--verbose")
0290 .default_value(false)
0291 .implicit_value(true)
0292 .help("Display details for every slice.");
0293
0294 try {
0295 args.parse_args(argc, argv);
0296 }
0297 catch (const std::runtime_error& err) {
0298 std::cout << err.what() << std::endl;
0299 std::cout << args;
0300 exit(0);
0301 }
0302
0303 signalFile = args.get<std::string>("--signalFile");
0304 signalFreq = args.get<double>("--signalFreq");
0305 signalSkip = args.get<int>("--signalSkip");
0306 signalStatus = args.get<int>("--signalStatus");
0307 backgroundFiles = parse_backgrounds(args.get<std::vector<std::string>>("--bgFile"));
0308 outputFile = args.get<std::string>("--outputFile");
0309 rootFormat = args.get<bool>("--rootFormat");
0310 intWindow = args.get<double>("--intWindow");
0311 nSlices = args.get<int>("--nSlices");
0312 squashTime = args.get<bool>("--squashTime");
0313 rngSeed = args.get<int>("--rngSeed");
0314 verbose = args.get<bool>("--verbose");
0315
0316 }
0317
0318
0319 void banner() {
0320
0321 std::cout << "==================================================================" << std::endl;
0322 std::cout << "=== EPIC HEPMC MERGER ===" << std::endl;
0323 std::cout << "authors: Benjamen Sterwerf* (bsterwerf@berkeley.edu), Kolja Kauder** (kkauder@bnl.gov), Reynier Cruz-Torres***" << std::endl;
0324 std::cout << "* University of California, Berkeley" << std::endl;
0325 std::cout << "** Brookhaven National Laboratory" << std::endl;
0326 std::cout << "*** formerly Lawrence Berkeley National Laboratory" << std::endl;
0327 std::cout << "\nFor more information, run \n./signal_background_merger --help" << std::endl;
0328
0329 std::string statusMessage = "Shifting all particle status codes from this source by ";
0330 std::vector<int> statusList_stable, statusList_decay;
0331
0332 std::cout << "Number of Slices:" << nSlices << endl;
0333 std::string freqTerm = signalFreq > 0 ? std::to_string(signalFreq) + " kHz" : "(one event per time slice)";
0334 std::string statusTerm = signalStatus > 0 ? statusMessage + std::to_string(signalStatus): "";
0335 if (signalStatus>0){
0336 statusList_stable.push_back(signalStatus+1);
0337 statusList_decay.push_back(signalStatus+2);
0338 }
0339 std::cout << "Signal events file and frequency:\n";
0340 std::cout << "\t- " << signalFile << "\t" << freqTerm << "\n" << statusTerm << "\n";
0341
0342 std::cout << "\nBackground files and their respective frequencies:\n";
0343
0344 for (const auto& bg : backgroundFiles) {
0345 if (!bg.file.empty()) {
0346 freqTerm = bg.frequency > 0 ? std::to_string(bg.frequency) + " kHz" : "(from weights)";
0347 statusTerm = bg.status > 0 ? statusMessage + std::to_string(bg.status) : "";
0348 std::cout << "\t- " << bg.file << "\t" << freqTerm << "\n" << statusTerm << "\n";
0349 if (bg.status>0){
0350 statusList_stable.push_back(bg.status+1);
0351 statusList_decay.push_back(bg.status+2);
0352 }
0353 }
0354 }
0355
0356 auto join = [](const std::vector<int>& vec) {
0357 return std::accumulate(vec.begin(), vec.end(), std::string(),
0358 [](const std::string& a, int b) {
0359 return a.empty() ? std::to_string(b) : a + " " + std::to_string(b);
0360 });
0361 };
0362 std::string stableStatuses = join(statusList_stable);
0363 std::string decayStatuses = join(statusList_decay);
0364 std::string message = "\n!!!Attention!!!\n To proceed the shifted particles statuses in DD4hep, please add the following options to ddsim:\n"
0365 "--physics.alternativeStableStatuses=\"" + stableStatuses +
0366 "\" --physics.alternativeDecayStatuses=\"" + decayStatuses + "\"\n";
0367 std::cout << message<<std::endl;
0368 }
0369
0370
0371 void PrepData(const std::string& fileName, double freq, int skip=0, int baseStatus=0, bool signal=false) {
0372 if (fileName.empty()) return;
0373
0374 cout << "Prepping " << fileName << endl;
0375 std::shared_ptr<HepMC3::Reader> adapter;
0376 try {
0377 adapter = HepMC3::deduce_reader(fileName);
0378 if (!adapter) {
0379 throw std::runtime_error("Failed to open file");
0380 }
0381 } catch (const std::runtime_error& e) {
0382 std::cerr << "Opening " << fileName << " failed: " << e.what() << std::endl;
0383 exit(1);
0384 }
0385
0386 infoDict[fileName] = {0,0};
0387
0388 if (signal) {
0389 sigAdapter = adapter;
0390 sigFreq = freq;
0391 sigStatus = baseStatus;
0392 sigAdapter->skip(skip);
0393 return;
0394 }
0395
0396
0397 if (freq <= 0) {
0398 std::cout << "Reading in all events from " << fileName << std::endl;
0399 std::vector<HepMC3::GenEvent> events;
0400 std::vector<double> weights;
0401
0402 while(!adapter->failed()) {
0403 HepMC3::GenEvent evt(HepMC3::Units::GEV,HepMC3::Units::MM);
0404 adapter->read_event(evt);
0405
0406
0407 if (double w=evt.weight() > 0){
0408 events.push_back(evt);
0409 weights.push_back(evt.weight());
0410 }
0411 }
0412 adapter->close();
0413
0414 double avgRate = 0.0;
0415 for ( auto w : weights ){ avgRate += w;}
0416 avgRate /= weights.size();
0417 avgRate *= 1e-9;
0418 std::cout << "Average rate is " << avgRate << " GHz" << std::endl;
0419
0420 std::vector<int> indices (weights.size());
0421 std::iota (std::begin(indices), std::end(indices), 0);
0422
0423
0424
0425
0426
0427
0428
0429
0430
0431
0432 std::piecewise_constant_distribution<> weightedDist(std::begin(indices),std::end(indices),
0433 std::begin(weights));
0434 weightDict[fileName] = { std::make_tuple(events, weightedDist, avgRate) };
0435
0436 return;
0437 }
0438
0439
0440 adapter->skip(skip);
0441 freqAdapters[fileName] = adapter;
0442 freqs[fileName] = freq;
0443 baseStatuses[fileName] = baseStatus;
0444 }
0445
0446
0447 bool hasEnding (std::string const &fullString, std::string const &ending) {
0448 if (fullString.length() >= ending.length()) {
0449 return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
0450 } else {
0451 return false;
0452 }
0453 }
0454
0455
0456 std::string nameGen() {
0457
0458
0459 std::string name = signalFile;
0460 if (nSlices > 0) {
0461 size_t pos = name.find(".hepmc");
0462 if (pos != std::string::npos) {
0463 name.replace(pos, 6, "_n_" + std::to_string(nSlices) + ".hepmc");
0464 }
0465 }
0466
0467 if ( rootFormat && !hasEnding(name,".root")){
0468 name.append(".root");
0469 }
0470 name = "bgmerged_" + name;
0471
0472 return name;
0473 }
0474
0475
0476 void squawk(int i) {
0477
0478
0479 #ifdef __MACH__
0480 task_basic_info_data_t info;
0481 mach_msg_type_number_t size = sizeof(info);
0482 kern_return_t kerr = task_info(mach_task_self(),
0483 TASK_BASIC_INFO,
0484 (task_info_t)&info,
0485 &size);
0486
0487 long memory_usage = -1;
0488 if (kerr == KERN_SUCCESS) {
0489 memory_usage = info.resident_size / 1024 / 1024;
0490 }
0491 #else
0492 std::ifstream statm("/proc/self/statm");
0493 long size, resident, share, text, lib, data, dt;
0494 statm >> size >> resident >> share >> text >> lib >> data >> dt;
0495 statm.close();
0496
0497 long page_size = sysconf(_SC_PAGESIZE);
0498 long memory_usage = resident * page_size / 1024 / 1024 ;
0499 #endif
0500
0501
0502 std::cout << "Working on slice " << i + 1 << std::endl;
0503 std::cout << "Current memory usage: " << memory_usage << " MB" << std::endl;
0504
0505 }
0506
0507
0508 std::unique_ptr<HepMC3::GenEvent> mergeSlice(int i) {
0509 auto hepSlice = std::make_unique<HepMC3::GenEvent>(HepMC3::Units::GEV, HepMC3::Units::MM);
0510
0511 addFreqEvents(signalFile, sigAdapter, sigFreq, hepSlice, signalStatus, true);
0512
0513 for (const auto& freqBgs : freqAdapters) {
0514 auto fileName=freqBgs.first;
0515 addFreqEvents(fileName, freqAdapters[fileName], freqs[fileName], hepSlice, baseStatuses[fileName], false);
0516 }
0517
0518 for (const auto& fileName : weightDict) {
0519 addWeightedEvents(fileName.first, hepSlice, baseStatuses[fileName.first]);
0520 }
0521
0522 return hepSlice;
0523 };
0524
0525
0526
0527 void addFreqEvents(std::string fileName, std::shared_ptr<HepMC3::Reader>& adapter, const double freq,
0528 std::unique_ptr<HepMC3::GenEvent>& hepSlice, int baseStatus = 0, bool signal = false) {
0529
0530
0531
0532 std::vector<double> timeline;
0533
0534 std::uniform_real_distribution<> uni(0, intWindow);
0535 if (freq == 0){
0536 if (!signal) {
0537 std::cerr << "frequency can't be 0 for background files" << std::endl;
0538 exit(1);
0539 }
0540
0541 timeline.push_back(uni(rng));
0542 } else {
0543
0544 timeline = poissonTimes(freq, intWindow);
0545 }
0546
0547 if ( verbose) std::cout << "Placing " << timeline.size() << " events from " << fileName << std::endl;
0548
0549 if (timeline.empty()) return;
0550 long particleCount = 0;
0551
0552
0553 for (double time : timeline) {
0554 if(adapter->failed()) {
0555 try{
0556 if (signal) {
0557 throw std::ifstream::failure("EOF");
0558 } else {
0559 std::cout << "Cycling back to the start of " << fileName << std::endl;
0560 adapter->close();
0561 adapter = HepMC3::deduce_reader(fileName);
0562 }
0563 } catch (std::ifstream::failure& e) {
0564 continue;
0565 }
0566 }
0567
0568 HepMC3::GenEvent inevt;
0569 adapter->read_event(inevt);
0570
0571 if (squashTime) time = 0;
0572 particleCount += insertHepmcEvent( inevt, hepSlice, time, baseStatus, signal);
0573 }
0574
0575 infoDict[fileName].eventCount += timeline.size();
0576 infoDict[fileName].particleCount += particleCount;
0577
0578
0579 return;
0580 }
0581
0582
0583
0584 void addWeightedEvents(std::string fileName, std::unique_ptr<HepMC3::GenEvent>& hepSlice, int baseStatus=0, bool signal = false) {
0585 auto& [events, weightedDist, avgRate ] = weightDict[fileName];
0586
0587
0588 int nEvents;
0589 std::poisson_distribution<> d( intWindow * avgRate );
0590
0591
0592
0593 while (true) {
0594 nEvents = d(rng);
0595 if (nEvents > events.size()) {
0596 std::cout << "WARNING: Trying to place " << nEvents << " events from " << fileName
0597 << " but the file doesn't have enough. Rerolling, but this is not physical." << std::endl;
0598 continue;
0599 }
0600 break;
0601 }
0602
0603 if (verbose) std::cout << "Placing " << nEvents << " events from " << fileName << std::endl;
0604
0605
0606
0607 std::vector<HepMC3::GenEvent> toPlace(nEvents);
0608 for ( auto& e : toPlace ){
0609 auto i = static_cast<int> (weightedDist(rng));
0610 e = events.at(i);
0611 }
0612
0613
0614 std::vector<double> timeline;
0615 std::uniform_real_distribution<> uni(0, intWindow);
0616 long particleCount = 0;
0617 if (!squashTime) {
0618 for ( auto& e : toPlace ){
0619 double time = squashTime ? 0 : uni(rng);
0620 particleCount += insertHepmcEvent( e, hepSlice, time, baseStatus, signal);
0621 }
0622 }
0623
0624 infoDict[fileName].eventCount += nEvents;
0625 infoDict[fileName].particleCount += particleCount;
0626
0627 return;
0628 }
0629
0630
0631 long insertHepmcEvent( const HepMC3::GenEvent& inevt,
0632 std::unique_ptr<HepMC3::GenEvent>& hepSlice, double time=0, int baseStatus=0, bool signal = false) {
0633
0634 double timeHepmc = c_light * time;
0635
0636 std::vector<HepMC3::GenParticlePtr> particles;
0637 std::vector<HepMC3::GenVertexPtr> vertices;
0638
0639
0640
0641 for (auto& vertex : inevt.vertices()) {
0642 HepMC3::FourVector position = vertex->position();
0643 position.set_t(position.t() + timeHepmc);
0644 auto v1 = std::make_shared<HepMC3::GenVertex>(position);
0645 vertices.push_back(v1);
0646 }
0647
0648
0649 long finalParticleCount = 0;
0650 for (auto& particle : inevt.particles()) {
0651 HepMC3::FourVector momentum = particle->momentum();
0652 int status = particle->status();
0653 if (status == 1 ) finalParticleCount++;
0654 int pid = particle->pid();
0655 status += baseStatus;
0656 auto p1 = std::make_shared<HepMC3::GenParticle> (momentum, pid, status);
0657 p1->set_generated_mass(particle->generated_mass());
0658 particles.push_back(p1);
0659
0660 if (particle->production_vertex()->id() < 0) {
0661 int production_vertex = particle->production_vertex()->id();
0662 vertices[abs(production_vertex) - 1]->add_particle_out(p1);
0663 hepSlice->add_particle(p1);
0664 }
0665
0666
0667 if (particle->end_vertex()) {
0668 int end_vertex = particle->end_vertex()->id();
0669 vertices.at(abs(end_vertex) - 1)->add_particle_in(p1);
0670 }
0671 }
0672
0673
0674 for (auto& vertex : vertices) {
0675 hepSlice->add_vertex(vertex);
0676 }
0677
0678 return finalParticleCount;
0679 }
0680
0681
0682
0683 std::vector<double> poissonTimes(double mu, double endTime) {
0684 std::exponential_distribution<> exp(mu);
0685
0686 double t = 0;
0687 std::vector<double> ret;
0688 while (true) {
0689 double delt = exp(rng)*1e6;
0690
0691 t += delt;
0692 if (t >= endTime) {
0693 break;
0694 }
0695 ret.push_back(t);
0696 }
0697 return ret;
0698 }
0699
0700
0701
0702
0703 std::mt19937 rng;
0704 string signalFile;
0705 double signalFreq;
0706 int signalSkip;
0707 int signalStatus;
0708 std::vector<BackgroundConfig> backgroundFiles;
0709 string outputFile;
0710 string outputFileName;
0711 bool rootFormat;
0712 double intWindow;
0713 int nSlices;
0714 bool squashTime;
0715 int rngSeed;
0716 bool verbose;
0717
0718 const double c_light = 299.792458;
0719 };
0720
0721
0722 int main(int argc, char* argv[]) {
0723
0724 auto t0 = std::chrono::high_resolution_clock::now();
0725
0726 SignalBackgroundMerger sbm (argc, argv);
0727
0728
0729 sbm.merge();
0730
0731 std::cout << "\n==================================================================\n";
0732 std::cout << "Overall running time: " << std::round(std::chrono::duration<double, std::chrono::minutes::period>(std::chrono::high_resolution_clock::now() - t0).count()) << " min" << std::endl;
0733
0734 return 0;
0735 }