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 Double_t chunk_seconds; // the run's epoch's chunk length
85};
86
88 Int_t nSplit;
89 Int_t nSkipped;
90 Int_t nMissing;
92};
93
94SplitResult SplitWorker(std::queue<WorkItem> &work, std::mutex &work_mutex,
95 const char *outputDir) {
96 SplitResult result;
97 result.nSplit = 0;
98 result.nSkipped = 0;
99 result.nMissing = 0;
100 result.nAlreadySplit = 0;
101
102 std::mutex log_mutex;
103
104 while (true) {
105 WorkItem item;
106 {
107 std::lock_guard<std::mutex> lk(work_mutex);
108 if (work.empty())
109 break;
110 item = work.front();
111 work.pop();
112 }
113
114 FileSpec spec;
115 spec.run = item.run;
116 spec.suffix = item.suffix;
117 TString solPath = FileSet::SolBinPath(spec);
118
119 if (gSystem->AccessPathName(solPath)) {
120 {
121 std::lock_guard<std::mutex> lk(log_mutex);
122 std::cerr << " [missing] " << solPath.Data() << std::endl;
123 }
124 result.nMissing++;
125 continue;
126 }
127
128 TString baseName = solPath;
129 Int_t lastSlash = baseName.Last('/');
130 if (lastSlash >= 0) {
131 baseName = baseName(lastSlash + 1, baseName.Length() - lastSlash - 1);
132 }
133
134 // Check if already split
135 TString chunk0Path = TString(outputDir) + "/" + baseName + "_chunk000.sol";
136 if (!gSystem->AccessPathName(chunk0Path)) {
137 // Counted in the closing summary; one line per file is noise on a
138 // re-run over hundreds of subfiles.
139 result.nAlreadySplit++;
140 continue;
141 }
142
143 // Check format
144 if (!IsMinimumFormat(solPath.Data())) {
145 {
146 std::lock_guard<std::mutex> lk(log_mutex);
147 std::cerr << " [skip] Not Minimum format: " << baseName << std::endl;
148 }
149 result.nSkipped++;
150 continue;
151 }
152
153 Int_t totalBlocks = 0;
154 Int_t totalChunks = 0;
155 std::vector<TString> outputFiles = SOLReader::SplitSolFileByTime(
156 solPath.Data(), outputDir, item.chunk_seconds, totalBlocks,
157 totalChunks);
158
159 if (outputFiles.empty()) {
160 {
161 std::lock_guard<std::mutex> lk(log_mutex);
162 std::cerr << " [error] Failed to split: " << solPath.Data()
163 << std::endl;
164 }
165 result.nSkipped++;
166 continue;
167 }
168
169 {
170 std::lock_guard<std::mutex> lk(log_mutex);
171 std::cout << " [split] " << baseName << ": " << totalBlocks
172 << " blocks -> " << totalChunks << " chunks" << std::endl;
173 }
174 result.nSplit++;
175 }
176
177 return result;
178}
179
180int main(int argc, char *argv[]) {
181 // A chunk length on the command line overrides every epoch's own.
182 Double_t forcedSeconds = 0.0;
183 Bool_t forced = kFALSE;
184 Int_t nWorkers = Constants::cfg.SOL_N_SPLIT_WORKERS;
185
186 if (argc >= 2) {
187 forcedSeconds = std::stod(argv[1]);
188 forced = kTRUE;
189 }
190 if (argc >= 3) {
191 nWorkers = std::stoi(argv[2]);
192 }
193
194 // The runs to split: every enabled SOLARIS epoch's runs at the epoch's
195 // chunk seconds (non-positive = no splitting), or the flat list.
196 std::vector<std::pair<Int_t, Double_t>> runs; // run, chunk seconds
197 if (Constants::cfg.EPOCHS.empty()) {
198 const Double_t sec =
199 forced ? forcedSeconds : Constants::cfg.SOL_SPLIT_CHUNK_SECONDS;
200 for (Int_t r = 0; r < Int_t(Constants::cfg.RUN_NUMBERS.size()); r++)
201 runs.push_back(std::make_pair(Constants::cfg.RUN_NUMBERS[r], sec));
202 } else {
203 for (Int_t e = 0; e < Int_t(Constants::cfg.EPOCHS.size()); e++) {
204 const RunEpoch &ep = Constants::cfg.EPOCHS[e];
205 if (!ep.enabled || ep.source != kSOLARIS)
206 continue;
207 const Double_t sec = forced ? forcedSeconds : ep.split_chunk_seconds;
208 std::cout << "epoch " << ep.name << ": " << ep.runs.size() << " run(s), ";
209 if (sec > 0)
210 std::cout << sec << "s chunks" << std::endl;
211 else
212 std::cout << "no splitting (whole files)" << std::endl;
213 for (Int_t r = 0; r < Int_t(ep.runs.size()); r++)
214 runs.push_back(std::make_pair(ep.runs[r], sec));
215 }
216 }
217 std::queue<WorkItem> work;
218 for (Int_t r = 0; r < Int_t(runs.size()); r++) {
219 if (!(runs[r].second > 0))
220 continue;
221 // A run declared twice takes its first epoch's length.
222 Bool_t seen = kFALSE;
223 for (Int_t q = 0; q < r && !seen; q++)
224 seen = runs[q].first == runs[r].first;
225 if (seen)
226 continue;
227 std::vector<TString> suffixes =
228 DiscoverSolRunSuffixesFromBase(runs[r].first);
229 for (Int_t k = 0; k < Int_t(suffixes.size()); k++) {
230 WorkItem item;
231 item.run = runs[r].first;
232 item.suffix = suffixes[k];
233 item.chunk_seconds = runs[r].second;
234 work.push(item);
235 }
236 }
237 if (work.empty()) {
238 std::cout << "SOLARIS preprocessing: nothing to split; the pipeline reads "
239 "the whole files from "
240 << Constants::cfg.SOL_BASE_DIR.Data() << std::endl;
241 return 0;
242 }
243 std::cout << "SOLARIS preprocessing: splitting Minimum files (" << nWorkers
244 << " workers)" << std::endl;
245 std::cout << "Input dir: " << Constants::cfg.SOL_BASE_DIR.Data()
246 << std::endl;
247 std::cout << "Output dir: " << Constants::cfg.SOL_SPLIT_DIR.Data()
248 << std::endl;
249 std::cout << std::endl;
250
251 gSystem->mkdir(Constants::cfg.SOL_SPLIT_DIR, kTRUE);
252
253 std::cout << "Total files to process: " << work.size() << std::endl;
254
255 // Launch workers
256 std::mutex work_mutex;
257 std::vector<std::thread> workers;
258 std::vector<SplitResult> results(nWorkers);
259
260 for (Int_t w = 0; w < nWorkers; w++) {
261 workers.emplace_back([&work, &work_mutex, &results, w]() {
262 results[w] =
263 SplitWorker(work, work_mutex, Constants::cfg.SOL_SPLIT_DIR.Data());
264 });
265 }
266
267 for (Int_t w = 0; w < nWorkers; w++) {
268 workers[w].join();
269 }
270
271 Int_t totalSplit = 0;
272 Int_t totalSkipped = 0;
273 Int_t totalMissing = 0;
274 Int_t totalAlreadySplit = 0;
275 for (Int_t w = 0; w < nWorkers; w++) {
276 totalSplit += results[w].nSplit;
277 totalSkipped += results[w].nSkipped;
278 totalMissing += results[w].nMissing;
279 totalAlreadySplit += results[w].nAlreadySplit;
280 }
281
282 std::cout << std::endl;
283 std::cout << "Preprocessing complete:" << std::endl;
284 std::cout << " Split: " << totalSplit << std::endl;
285 std::cout << " Already split:" << totalAlreadySplit << std::endl;
286 std::cout << " Skipped: " << totalSkipped << std::endl;
287 std::cout << " Missing: " << totalMissing << std::endl;
288
289 return (totalMissing > 0) ? 1 : 0;
290}
Dataset configuration.
@ kSOLARIS
SOLARIS DAQ.
Definition RunEpoch.hpp:16
static TString SolBinPath(const FileSpec &s)
Path to a SOLARIS .sol subfile.
Definition FileSet.cpp:11
int main()
compute-regions: fit each reaction strip's (a,n) and (a,a') regions from the scatter cache and save t...
SplitResult SplitWorker(std::queue< WorkItem > &work, std::mutex &work_mutex, const char *outputDir)
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:34
std::vector< Int_t > runs
Run numbers belonging to this epoch.
Definition RunEpoch.hpp:48
TString name
Epoch name, used in logs and plot paths.
Definition RunEpoch.hpp:35
Bool_t enabled
Whether this epoch participates in the analysis.
Definition RunEpoch.hpp:37
Double_t split_chunk_seconds
Definition RunEpoch.hpp:91
RunSource source
Acquisition system for this era.
Definition RunEpoch.hpp:36
Double_t chunk_seconds