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