45#include <fairlogger/Logger.h>
47#include <TFileMerger.h>
50#include <TObjString.h>
52#include <boost/program_options.hpp>
63namespace fs = std::filesystem;
67const char* kTrackBranch =
"MCTrack";
68const char* kHeaderBranch =
"MCEventHeader.";
69const char* kTrackRefBranch =
"TrackRefs";
70const char* kProtocol =
"alien://";
72bool isAlienPath(std::string
const&
path)
83 LOG(info) <<
"Connecting to AliEn ...";
84 if (!TGrid::Connect(
"alien:") || !gGrid) {
85 LOG(
error) <<
"Could not connect to AliEn; check your alien token";
92std::optional<std::vector<std::string>> readLocalListFileLines(std::string
const&
path)
94 std::ifstream in(
path);
98 std::vector<std::string> lines;
100 while (std::getline(in, line)) {
101 lines.push_back(line);
110size_t expandInputEntry(std::string
const& rawEntry, std::vector<std::string>& out, std::vector<std::string>&
stack)
115 out.push_back(
entry);
119 LOG(
error) <<
"Reference to an existing list " <<
entry <<
"; ignoring";
122 auto lines = readLocalListFileLines(
entry);
124 LOG(
error) <<
"Cannot open " <<
entry <<
" (neither a .root file nor a readable local list)";
128 size_t unresolved = 0;
129 for (
auto line : *lines) {
131 if (
line.empty() || line[0] ==
'#') {
134 unresolved += expandInputEntry(line, out,
stack);
142size_t expandInputs(std::vector<std::string>
const& rawEntries, std::vector<std::string>& infiles)
144 std::vector<std::string> resolved;
145 std::vector<std::string>
stack;
146 size_t unresolved = 0;
147 for (
auto const&
e : rawEntries) {
148 unresolved += expandInputEntry(
e, resolved,
stack);
150 std::set<std::string> seen;
151 for (
auto const&
f : resolved) {
152 if (seen.insert(
f).second) {
153 infiles.push_back(
f);
155 LOG(warning) <<
"Input " <<
f <<
" is listed more than once; merging it only once";
164std::string inspectFile(std::string
const&
path, std::string
const& treename,
165 Long64_t&
entries,
int& compression)
167 std::unique_ptr<TFile>
file(TFile::Open(
path.c_str(),
"READ"));
169 return "file does not exist or cannot be opened";
171 auto tree = (TTree*)
file->Get(treename.c_str());
173 return "no tree named '" + treename +
"' in the file";
175 if (
tree->GetBranch(kTrackBranch) ==
nullptr ||
tree->GetBranch(kHeaderBranch) ==
nullptr ||
176 tree->GetBranch(kTrackRefBranch) ==
nullptr) {
177 return std::string(
"missing the required '") + kTrackBranch +
"', '" + kHeaderBranch +
"' and/or '" +
178 kTrackRefBranch +
"' branch";
181 compression =
file->GetCompressionSettings();
188bool checkFiles(std::vector<std::string>
const& files, std::string
const& treename,
189 std::vector<std::string>& usable, Long64_t& totalEvents,
int& compression)
194 for (
auto const&
f : files) {
196 int fileCompression = -1;
197 const auto issue = inspectFile(
f, treename,
entries, fileCompression);
198 if (!issue.empty()) {
199 LOG(
error) <<
"Input file " <<
f <<
": " << issue;
203 if (compression < 0) {
204 compression = fileCompression;
208 LOG(info) <<
" OK " <<
f <<
" (" <<
entries <<
" events)";
214void writeMergeInfo(std::string
const& outfile, std::vector<std::string>
const& requested,
215 std::vector<std::string>
const& merged,
size_t unresolved, Long64_t
events)
217 std::unique_ptr<TFile>
file(TFile::Open(outfile.c_str(),
"UPDATE"));
219 LOG(warning) <<
"Cannot add the merge information to " << outfile;
224 std::string mergedList, skippedList;
225 for (
auto const&
f : requested) {
226 if (std::find(merged.begin(), merged.end(),
f) != merged.end()) {
227 mergedList +=
f +
"\n";
229 skippedList +=
f +
"\n";
233 info.SetOwnerKeyValue();
234 info.Add(
new TObjString(
"inputsRequested"),
new TObjString(
std::to_string(requested.size()).c_str()));
235 info.Add(
new TObjString(
"inputsMerged"),
new TObjString(
std::to_string(merged.size()).c_str()));
236 info.Add(
new TObjString(
"inputsUnresolved"),
new TObjString(
std::to_string(unresolved).c_str()));
238 info.Add(
new TObjString(
"mergedFiles"),
new TObjString(mergedList.c_str()));
239 info.Add(
new TObjString(
"skippedFiles"),
new TObjString(skippedList.c_str()));
241 info.Write(
"mergeInfo", TObject::kSingleKey);
246bool validateOutput(std::string
const& outfile, std::string
const& treename, Long64_t
expected)
249 int compression = -1;
250 const auto issue = inspectFile(outfile, treename,
entries, compression);
251 if (!issue.empty()) {
252 LOG(
error) <<
"Merged file " << outfile <<
" is not usable: " << issue;
257 <<
" were merged into it";
264int main(
int argc,
char* argv[])
266 bpo::options_description options(
"o2-generators-merge-evtpool options");
267 auto add = options.add_options();
268 add(
"input,i", bpo::value<std::string>()->required(),
269 "comma-separated list of inputs: event-pool ROOT files (local or alien://), and/or "
270 "local text files listing more paths (one per line, '#' comments allowed)");
271 add(
"output,o", bpo::value<std::string>()->default_value(
"evtpool.root"),
272 "output ROOT file with the merged event pool");
273 add(
"check-tree,t", bpo::value<std::string>()->default_value(
"o2sim"),
274 "name of the tree the inputs and the merged pool are checked against; everything the "
275 "input files contain is merged regardless");
276 add(
"skip-non-existing-files", bpo::bool_switch(),
277 "skip inputs that cannot be resolved or opened instead of aborting the merge");
278 add(
"help,h",
"produce help message");
279 bpo::variables_map vm;
281 bpo::store(bpo::parse_command_line(argc, argv, options), vm);
282 if (vm.count(
"help")) {
283 LOG(info) << options;
287 }
catch (
const bpo::error&
e) {
288 LOG(
error) <<
"Error parsing command-line arguments: " <<
e.what() <<
"\n\n"
293 if (rawEntries.empty()) {
294 LOG(
error) <<
"No input files given";
298 const bool skipMissing = vm[
"skip-non-existing-files"].as<
bool>();
299 std::vector<std::string> infiles;
300 const size_t unresolved = expandInputs(rawEntries, infiles);
301 if (unresolved > 0 && !skipMissing) {
302 LOG(
error) <<
"Some --input entries could not be resolved; "
303 "pass --skip-non-existing-files to merge the rest anyway";
306 if (infiles.empty()) {
307 LOG(
error) <<
"No input files resolved from the given --input entries";
311 if (std::any_of(infiles.begin(), infiles.end(), isAlienPath) && !GridOn()) {
312 LOG(
error) <<
"Some inputs live on AliEn but the grid is not available";
315 const std::string outfile = vm[
"output"].as<std::string>();
316 const std::string treename = vm[
"check-tree"].as<std::string>();
317 LOG(info) <<
"Validating " << infiles.size() <<
" input file(s) ...";
318 std::vector<std::string> usable;
319 Long64_t totalEvents = 0;
320 int compression = -1;
321 if (!
checkFiles(infiles, treename, usable, totalEvents, compression) && !skipMissing) {
322 LOG(
error) <<
"Validation failed; not writing any output "
323 "(pass --skip-non-existing-files to merge the rest anyway)";
326 if (usable.empty()) {
327 LOG(
error) <<
"None of the input files could be used; not writing any output";
333 const std::string partfile = outfile +
".part";
334 auto discardPart = [&partfile]() {
336 fs::remove(partfile, ec);
340 LOG(info) <<
"Merging " << totalEvents <<
" events from " << usable.size() <<
" file(s) into "
341 << outfile <<
" ...";
343 TFileMerger
merger(
false,
false);
345 if (!
merger.OutputFile(partfile.c_str(),
"RECREATE", compression)) {
346 LOG(
error) <<
"Cannot create output file " << partfile;
347 return discardPart();
349 for (
auto const&
f : usable) {
350 if (!
merger.AddFile(
f.c_str())) {
351 LOG(
error) <<
"Cannot add " <<
f <<
" to the merge";
352 return discardPart();
356 LOG(
error) <<
"Merging failed; no output written";
357 return discardPart();
361 writeMergeInfo(partfile, infiles, usable, unresolved, totalEvents);
362 if (!validateOutput(partfile, treename, totalEvents)) {
363 LOG(
error) <<
"The merged pool did not pass the final check; no output written";
364 return discardPart();
368 fs::rename(partfile, outfile, ec);
370 LOG(
error) <<
"Cannot move " << partfile <<
" to " << outfile <<
": " << ec.message();
371 return discardPart();
374 LOG(info) <<
"Done: wrote " << totalEvents <<
" events from " << usable.size() <<
" of "
375 << infiles.size() <<
" input file(s) to " << outfile;
std::vector< long > entries
configStandalone Light OpenGL display display_light Apply smoothing to points Enable Z buffer Highlight links Highlight seeds Highlight sector tracks Highlight final tracks Show individual Show related Highlight grid Show only individual collision Color clusters belonging to track objects Set line Enable drawing TPC data Enable drawing TOF data Enable drawing magnetic field Set field line step count Invert colors configStandalone Heavy OpenGL display display_heavy Show tracks with ITS contribution Show tracks with TOF contribution Enable propagation of loopers Show clusters at nominal x of pad row for early transformed data Split CE tracks when they cross the central Mark clusters Mark adjacent clusters Hide rejected tracks Apply filter on tracks to be displayed Separate increase X h configStandalone Camera window renderer OpenGL display display_camera Orient the camera such that the y axis is always upwards Full Screen MultiSample Anti Downsampling Anti Enable Vertical Sync Target frame or if the backend cannot limit to the refresh Use OpenGL indirect draws to reduce number of draw calls Display h configStandalone Vulkan display display_vulkan Have uniform buffers in host accessible device memory h configStandalone Internal display display_internal Number of updates the Qt GUI may send ahead of the last one the display has before it waits for the display Opacity of the console background Number of console lines the history is shortened to this when it reaches twice the size Time in seconds an info message fades out after setInfoTime h OpenGL display display Font size Do not use Freetype for font Renderer for event Frontend to use for event ROOT macros used as track filter Console script file
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error First event to Number of events to Number of iterations to Number of iterations to Number of initial iterations excluded from const char events
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error First event to Number of events to Number of iterations to Number of iterations to Number of initial iterations excluded from const char e
configStandalone Reconstruction rec_tpc multiplier for the combined cluster track error during track following area in cm for the search of for z only used if decrease removal by when above this local occupancy rowx Shift of TPC revert cluster flag edge bit distance to edge exceeds this ZOffset for secondary multiple of radius max P of electron K BB bands crossing max P of electron p BB bands crossing max P of electron d BB bands crossing max P of electron t BB bands crossing min accepted dEdxMaxTPC of the track Additive extra cluster error for Y2 if edge flag set Additive extra cluster error for Y2 if single set Additive extra cluster error for Y2 if or single set Additive extra cluster error for Z2 if or single set Extra errors to add to track when crossing depending on addErrorsCECrossing Inner portion of y range in sector that is not used in searching for extrapolated track candidates factor2YS for track merging factor2K for track merging Margin in cm of Y position when rejecting edge clusters based on uncorrected track Y Max fraction of shared clusters for tracklet Except pad from cluster finding if total number of charges in a fragment is above this Except pad from cluster finding if number of consecutive charges in a fragment is above this Threshold where a timebin is considered disabling the noisy pad check for that pad Treat signal above this minimum as saturated Smoothing factor for the exponential Highly Ionising Particle tail filter Number of timebins to use for the averaging Add additional custom track errors when crossing relative as fraction of Do N fit passes in final fit of merger(must be odd to end with inward fit)") AddOptionRTC(trackFitRejectMode
char error[CURL_ERROR_SIZE]
GLsizei const GLchar *const * path
int32_t const char int32_t line
std::string expandShellVarsInFileName(std::string const &input)
std::string to_string(gsl::span< T, Size > span)
static void trim(std::string &s)
static bool beginsWith(const std::string &s, const std::string &start)
static std::vector< std::string > tokenize(const std::string &src, char delim, bool trimToken=true, bool skipEmpty=true)
static bool endsWith(const std::string &s, const std::string &ending)
std::map< std::string, ID > expected
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
std::unique_ptr< TTree > tree((TTree *) flIn.Get(std::string(o2::base::NameConf::CTFTREENAME).c_str()))