Project
Loading...
Searching...
No Matches
G4ScoringMerger.cxx
Go to the documentation of this file.
1// Copyright 2019-2026 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
13#include <fairlogger/Logger.h>
14#include <filesystem>
15#include <fstream>
16#include <iomanip>
17#include <map>
18#include <regex>
19#include <sstream>
20#include <vector>
21
22namespace o2::conf
23{
24
25namespace
26{
27// One scorer block of a Geant4 mesh dump: its header lines and the summed rows
28struct ScorerBlock {
29 std::vector<std::string> header;
30 std::vector<std::string> keys; // "iZ,iPHI,iR" in file order
31 std::vector<double> sum;
32 std::vector<double> sum2;
33 std::vector<long> entries;
34};
35
36// Read one mesh dump into scorer blocks; returns false on a format error
37bool readDump(const std::string& fileName, std::vector<std::string>& meshHeader, std::vector<ScorerBlock>& blocks)
38{
39 std::ifstream in(fileName);
40 if (!in) {
41 return false;
42 }
43 std::string line;
44 ScorerBlock* current = nullptr;
45 while (std::getline(in, line)) {
46 if (line.rfind("# mesh name", 0) == 0) {
47 meshHeader.push_back(line);
48 } else if (line.rfind("# primitive scorer name", 0) == 0) {
49 blocks.emplace_back();
50 current = &blocks.back();
51 current->header.push_back(line);
52 } else if (line.rfind("#", 0) == 0) {
53 if (!current) {
54 return false;
55 }
56 current->header.push_back(line);
57 } else if (!line.empty()) {
58 if (!current) {
59 return false;
60 }
61 // iZ, iPHI, iR, total, total^2, entries
62 std::vector<std::string> fields;
63 std::stringstream ss(line);
64 std::string field;
65 while (std::getline(ss, field, ',')) {
66 fields.push_back(field);
67 }
68 if (fields.size() != 6) {
69 return false;
70 }
71 current->keys.push_back(fields[0] + "," + fields[1] + "," + fields[2]);
72 current->sum.push_back(std::stod(fields[3]));
73 current->sum2.push_back(std::stod(fields[4]));
74 current->entries.push_back(std::stol(fields[5]));
75 }
76 }
77 return !blocks.empty();
78}
79} // namespace
80
81std::string g4ScoringWorkerFileName(const std::string& meshName, int pid)
82{
83 return meshName + ".worker" + std::to_string(pid) + ".txt";
84}
85
86int mergeG4ScoringDumps(const std::string& directory, int expectedWorkers)
87{
88 namespace fs = std::filesystem;
89 const std::regex pattern(R"((.+)\.worker([0-9]+)\.txt)");
90 std::map<std::string, std::vector<fs::path>> filesPerMesh;
91 for (auto& entry : fs::directory_iterator(directory)) {
92 std::smatch match;
93 const auto name = entry.path().filename().string();
94 if (entry.is_regular_file() && std::regex_match(name, match, pattern)) {
95 filesPerMesh[match[1]].push_back(entry.path());
96 }
97 }
98
99 int merged = 0;
100 for (auto& [mesh, files] : filesPerMesh) {
101 if (expectedWorkers > 0 && static_cast<int>(files.size()) != expectedWorkers) {
102 LOG(error) << "Found " << files.size() << " Geant4 scoring dumps for mesh " << mesh << " but expected " << expectedWorkers;
103 return -1;
104 }
105 std::vector<std::string> meshHeader;
106 std::vector<ScorerBlock> total;
107 for (auto& file : files) {
108 std::vector<std::string> header;
109 std::vector<ScorerBlock> blocks;
110 if (!readDump(file.string(), header, blocks)) {
111 LOG(error) << "Cannot read Geant4 scoring dump " << file;
112 return -1;
113 }
114 if (total.empty()) {
115 meshHeader = header;
116 total = std::move(blocks);
117 continue;
118 }
119 if (blocks.size() != total.size()) {
120 LOG(error) << "Geant4 scoring dump " << file << " has a different set of scorers";
121 return -1;
122 }
123 for (size_t b = 0; b < blocks.size(); ++b) {
124 if (blocks[b].header != total[b].header || blocks[b].keys != total[b].keys) {
125 LOG(error) << "Geant4 scoring dump " << file << " does not match the mesh layout of the other workers";
126 return -1;
127 }
128 for (size_t i = 0; i < blocks[b].keys.size(); ++i) {
129 total[b].sum[i] += blocks[b].sum[i];
130 total[b].sum2[i] += blocks[b].sum2[i];
131 total[b].entries[i] += blocks[b].entries[i];
132 }
133 }
134 }
135
136 const auto outName = (fs::path(directory) / (mesh + ".txt")).string();
137 std::ofstream out(outName);
138 out << std::setprecision(16);
139 for (auto& line : meshHeader) {
140 out << line << "\n";
141 }
142 for (auto& block : total) {
143 for (auto& line : block.header) {
144 out << line << "\n";
145 }
146 for (size_t i = 0; i < block.keys.size(); ++i) {
147 out << block.keys[i] << "," << block.sum[i] << "," << block.sum2[i] << "," << block.entries[i] << "\n";
148 }
149 }
150 LOG(info) << "Merged " << files.size() << " Geant4 scoring dumps into " << outName;
151 ++merged;
152 }
153 return merged;
154}
155
156} // namespace o2::conf
std::vector< std::shared_ptr< arrow::Field > > fields
std::vector< std::string > header
std::vector< long > entries
std::vector< double > sum
std::vector< double > sum2
std::vector< std::string > keys
int32_t i
uint16_t pid
Definition RawData.h:2
bool match(const std::vector< std::string > &queries, const char *pattern)
Definition dcs-ccdb.cxx:229
GLuint entry
Definition glcorearb.h:5735
GLuint const GLchar * name
Definition glcorearb.h:781
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
std::string g4ScoringWorkerFileName(const std::string &meshName, int pid)
Name of the Geant4 scoring dump written by one simulation worker.
int mergeG4ScoringDumps(const std::string &directory, int expectedWorkers=0)
int32_t const char int32_t line
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
std::array< uint16_t, 5 > pattern