MUSIC unknown
Analysis for the MUSIC active-target ionization chamber
Loading...
Searching...
No Matches
main_preprocess_sol.cpp
Go to the documentation of this file.
1#include "BinaryUtils.hpp"
2#include "Constants.hpp"
3#include "FileSet.hpp"
4#include <condition_variable>
5#include <iostream>
6#include <mutex>
7#include <queue>
8#include <set>
9#include <sstream>
10#include <thread>
11#include <vector>
12
13// Check if a SOL file uses Minimum format by reading the first block header.
14Bool_t IsMinimumFormat(const char *filePath) {
15 std::ifstream file(filePath, std::ios::binary);
16 if (!file.is_open()) {
17 return kFALSE;
18 }
19
20 UShort_t blockHeader;
21 file.read(reinterpret_cast<char *>(&blockHeader), sizeof(UShort_t));
22 if (file.fail()) {
23 file.close();
24 return kFALSE;
25 }
26 file.close();
27
28 if ((blockHeader & 0xAA00) != 0xAA00) {
29 return kFALSE;
30 }
31
32 UChar_t dataType = blockHeader & 0xF;
33 return dataType == SOLData::Minimum;
34}
35
36// Discover original SOL files for a run from the base dir (not split dir).
37std::vector<TString> DiscoverSolRunSuffixesFromBase(Int_t run) {
38 std::vector<TString> suffixes;
39 TString sol_dir = Constants::cfg.SOL_BASE_DIR;
40 void *dirp = gSystem->OpenDirectory(sol_dir);
41 if (!dirp) {
42 std::cerr << "DiscoverSolRunSuffixesFromBase: cannot open " << sol_dir
43 << std::endl;
44 return suffixes;
45 }
46
47 TString prefix = Form("music_exp1915_%03d_00_66222_", run);
48 const Char_t *name;
49 while ((name = gSystem->GetDirEntry(dirp))) {
50 TString fname(name);
51 if (!fname.BeginsWith(prefix))
52 continue;
53 if (!fname.EndsWith(".sol"))
54 continue;
55
56 TString rest = fname(prefix.Length(), fname.Length() - prefix.Length() - 4);
57 if (!rest.IsDigit())
58 continue;
59
60 Int_t seq = rest.Atoi();
61 if (seq == 0) {
62 suffixes.push_back("");
63 } else {
64 suffixes.push_back(Form("_%d", seq));
65 }
66 }
67 gSystem->FreeDirectory(dirp);
68
69 std::sort(suffixes.begin(), suffixes.end(),
70 [](const TString &a, const TString &b) {
71 if (a == "")
72 return true;
73 if (b == "")
74 return false;
75 return a.Atoi() < b.Atoi();
76 });
77
78 return suffixes;
79}
80
81struct WorkItem {
82 Int_t run;
83 TString suffix;
84};
85
87 Int_t nSplit;
88 Int_t nSkipped;
89 Int_t nMissing;
91};
92
93SplitResult SplitWorker(std::queue<WorkItem> &work, std::mutex &work_mutex,
94 const char *outputDir, Double_t chunkSeconds) {
95 SplitResult result;
96 result.nSplit = 0;
97 result.nSkipped = 0;
98 result.nMissing = 0;
99 result.nAlreadySplit = 0;
100
101 std::mutex log_mutex;
102
103 while (true) {
104 WorkItem item;
105 {
106 std::lock_guard<std::mutex> lk(work_mutex);
107 if (work.empty())
108 break;
109 item = work.front();
110 work.pop();
111 }
112
113 FileSpec spec;
114 spec.run = item.run;
115 spec.suffix = item.suffix;
116 TString solPath = FileSet::SolBinPath(spec);
117
118 if (gSystem->AccessPathName(solPath)) {
119 {
120 std::lock_guard<std::mutex> lk(log_mutex);
121 std::cerr << " [missing] " << solPath.Data() << std::endl;
122 }
123 result.nMissing++;
124 continue;
125 }
126
127 TString baseName = solPath;
128 Int_t lastSlash = baseName.Last('/');
129 if (lastSlash >= 0) {
130 baseName = baseName(lastSlash + 1, baseName.Length() - lastSlash - 1);
131 }
132
133 // Check if already split
134 TString chunk0Path = TString(outputDir) + "/" + baseName + "_chunk000.sol";
135 if (!gSystem->AccessPathName(chunk0Path)) {
136 {
137 std::lock_guard<std::mutex> lk(log_mutex);
138 std::cout << " [exists] " << baseName << std::endl;
139 }
140 result.nAlreadySplit++;
141 continue;
142 }
143
144 // Check format
145 if (!IsMinimumFormat(solPath.Data())) {
146 {
147 std::lock_guard<std::mutex> lk(log_mutex);
148 std::cerr << " [skip] Not Minimum format: " << baseName << std::endl;
149 }
150 result.nSkipped++;
151 continue;
152 }
153
154 {
155 std::lock_guard<std::mutex> lk(log_mutex);
156 std::cout << " [split] " << baseName << std::endl;
157 }
158
159 Int_t totalBlocks = 0;
160 Int_t totalChunks = 0;
161 std::vector<TString> outputFiles = SOLReader::SplitSolFileByTime(
162 solPath.Data(), outputDir, chunkSeconds, totalBlocks, totalChunks);
163
164 if (outputFiles.empty()) {
165 {
166 std::lock_guard<std::mutex> lk(log_mutex);
167 std::cerr << " [error] Failed to split: " << solPath.Data()
168 << std::endl;
169 }
170 result.nSkipped++;
171 continue;
172 }
173
174 {
175 std::lock_guard<std::mutex> lk(log_mutex);
176 std::cout << " " << totalBlocks << " blocks -> " << totalChunks
177 << " chunks" << std::endl;
178 }
179 result.nSplit++;
180 }
181
182 return result;
183}
184
185int main(int argc, char *argv[]) {
186 Double_t chunkSeconds = Constants::cfg.SOL_SPLIT_CHUNK_SECONDS;
187 Int_t nWorkers = Constants::cfg.SOL_N_SPLIT_WORKERS;
188
189 if (argc >= 2) {
190 chunkSeconds = std::stod(argv[1]);
191 }
192 if (argc >= 3) {
193 nWorkers = std::stoi(argv[2]);
194 }
195
196 std::cout << "SOLARIS preprocessing: splitting Minimum files into "
197 << chunkSeconds << "s chunks (" << nWorkers << " workers)"
198 << std::endl;
199 std::cout << "Input dir: " << Constants::cfg.SOL_BASE_DIR.Data()
200 << std::endl;
201 std::cout << "Output dir: " << Constants::cfg.SOL_SPLIT_DIR.Data()
202 << std::endl;
203 std::cout << std::endl;
204
205 gSystem->mkdir(Constants::cfg.SOL_SPLIT_DIR, kTRUE);
206
207 // Build work queue from base dir only
208 // Split every SOLARIS run the dataset declares. With epochs that means the
209 // union of the SOLARIS epochs' run lists; without them, the flat run list.
210 // A CoMPASS epoch has no .sol files to split and is skipped.
211 std::vector<Int_t> runs;
212 if (Constants::cfg.EPOCHS.empty()) {
213 runs = Constants::cfg.RUN_NUMBERS;
214 } else {
215 for (Int_t e = 0; e < Int_t(Constants::cfg.EPOCHS.size()); e++) {
216 const RunEpoch &ep = Constants::cfg.EPOCHS[e];
217 if (!ep.enabled || ep.source != kSolaris)
218 continue;
219 for (Int_t r = 0; r < Int_t(ep.runs.size()); r++)
220 runs.push_back(ep.runs[r]);
221 }
222 std::sort(runs.begin(), runs.end());
223 runs.erase(std::unique(runs.begin(), runs.end()), runs.end());
224 }
225
226 std::queue<WorkItem> work;
227 Int_t nRuns = runs.size();
228 for (Int_t r = 0; r < nRuns; r++) {
229 Int_t run = runs[r];
230 std::vector<TString> suffixes = DiscoverSolRunSuffixesFromBase(run);
231 for (Int_t k = 0; k < Int_t(suffixes.size()); k++) {
232 WorkItem item;
233 item.run = run;
234 item.suffix = suffixes[k];
235 work.push(item);
236 }
237 }
238
239 std::cout << "Total files to process: " << work.size() << std::endl;
240
241 // Launch workers
242 std::mutex work_mutex;
243 std::vector<std::thread> workers;
244 std::vector<SplitResult> results(nWorkers);
245
246 for (Int_t w = 0; w < nWorkers; w++) {
247 workers.emplace_back([&work, &work_mutex, &results, w, chunkSeconds]() {
248 results[w] = SplitWorker(
249 work, work_mutex, Constants::cfg.SOL_SPLIT_DIR.Data(), chunkSeconds);
250 });
251 }
252
253 for (Int_t w = 0; w < nWorkers; w++) {
254 workers[w].join();
255 }
256
257 Int_t totalSplit = 0;
258 Int_t totalSkipped = 0;
259 Int_t totalMissing = 0;
260 Int_t totalAlreadySplit = 0;
261 for (Int_t w = 0; w < nWorkers; w++) {
262 totalSplit += results[w].nSplit;
263 totalSkipped += results[w].nSkipped;
264 totalMissing += results[w].nMissing;
265 totalAlreadySplit += results[w].nAlreadySplit;
266 }
267
268 std::cout << std::endl;
269 std::cout << "Preprocessing complete:" << std::endl;
270 std::cout << " Split: " << totalSplit << std::endl;
271 std::cout << " Already split:" << totalAlreadySplit << std::endl;
272 std::cout << " Skipped: " << totalSkipped << std::endl;
273 std::cout << " Missing: " << totalMissing << std::endl;
274
275 return (totalMissing > 0) ? 1 : 0;
276}
The dataset configuration, and how it is layered.
@ kSolaris
SOLARIS DAQ.
Definition RunEpoch.hpp:15
static TString SolBinPath(const FileSpec &s)
Path to a SOLARIS .sol subfile.
Definition FileSet.cpp:11
int main()
SplitResult SplitWorker(std::queue< WorkItem > &work, std::mutex &work_mutex, const char *outputDir, Double_t chunkSeconds)
std::vector< TString > DiscoverSolRunSuffixesFromBase(Int_t run)
Bool_t IsMinimumFormat(const char *filePath)
const DatasetConfig & cfg
The active dataset's configuration, flat block.
One input file: a run number and the subfile suffix within it.
Definition FileSet.hpp:40
TString suffix
Subfile suffix, empty for the first subfile.
Definition FileSet.hpp:42
Int_t run
Run number.
Definition FileSet.hpp:41
One acquisition period of a dataset.
Definition RunEpoch.hpp:37
std::vector< Int_t > runs
Run numbers belonging to this epoch.
Definition RunEpoch.hpp:51
Bool_t enabled
Whether this epoch participates in the analysis.
Definition RunEpoch.hpp:40
RunSource source
Acquisition system for this era.
Definition RunEpoch.hpp:39