MUSIC unknown
Analysis for the MUSIC active-target ionization chamber
Loading...
Searching...
No Matches
Pipeline.cpp
Go to the documentation of this file.
1#include "Pipeline.hpp"
2#include "PulseHistory.hpp"
3
4std::mutex fused_log_mutex;
5
6Bool_t FusedExists(const TString &subpath) {
7 TString full = IO::GetRootFilesBaseDir() + "/" + subpath;
8 return !gSystem->AccessPathName(full);
9}
10
11Double_t FusedSecSince(const std::chrono::steady_clock::time_point &t0) {
12 return std::chrono::duration<Double_t>(std::chrono::steady_clock::now() - t0)
13 .count();
14}
15
16// /proc/self/statm reports VmRSS in pages (4 KiB/page on Linux); the
17// label is worker-local and only the per-file sample logs it.
18void PrintMemUsage(const char *label) {
20 return;
21 Long64_t rss = 0;
22 std::ifstream statm("/proc/self/statm");
23 Long64_t dummy;
24 statm >> dummy >> rss;
25 Double_t rss_gb = rss * 4096.0 / (1024.0 * 1024.0 * 1024.0);
26 std::lock_guard<std::mutex> lock(fused_log_mutex);
27 std::cout << "[MEM] " << label << ": " << rss_gb << " GB RSS" << std::endl;
28}
29
30Bool_t EnsureRunHeaderFused(Int_t run, UShort_t &header) {
31 if (BinaryToRoot::ReadHeaderFile(run, header))
32 return kTRUE;
33
35 // SOLARIS: find first available file (chunk or original) for header
36 std::vector<TString> suffixes = FileSet::DiscoverSolRunSuffixes(run);
37 Bool_t found = kFALSE;
38 for (Int_t k = 0; k < Int_t(suffixes.size()); k++) {
39 FileSpec s0;
40 s0.run = run;
41 s0.suffix = suffixes[k];
42 TString sol_path = FileSet::SolBinPath(s0);
43 if (gSystem->AccessPathName(sol_path))
44 continue;
45
46 SOLReader reader;
47 if (!reader.Open(sol_path.Data()))
48 continue;
49 if (!reader.ReadEvent()) {
50 reader.Close();
51 continue;
52 }
53 header = reader.GetCurrentEvent().block_header;
54 reader.Close();
55 found = kTRUE;
56 break;
57 }
58 if (!found) {
59 std::cerr << "SOL header gather FAILED for run " << run
60 << " (no accessible files)" << std::endl;
61 return kFALSE;
62 }
63 } else {
64 // CoMPASS: read global header from first .BIN file
65 FileSpec s0;
66 s0.run = run;
67 s0.suffix = "";
68 TString bin_path = FileSet::CompassBinPath(s0);
69 std::pair<std::vector<RawHit>, UShort_t> p =
70 InitUtils::ConvertCoMPASSBinToHits(bin_path, 0);
71 if (p.second == 0)
72 return kFALSE;
73 header = p.second;
74 }
75
77 return kTRUE;
78}
79
80Bool_t RunFusedPipelineForFile(FileSpec spec, UShort_t run_header,
81 const EventBuilder::SlotMap &slot_map,
82 const std::vector<ChannelCal> &chans) {
83 TString file_label = FileSet::FileLabel(spec);
84 std::chrono::steady_clock::time_point t_total =
85 std::chrono::steady_clock::now();
86 std::chrono::steady_clock::time_point t0;
87 Double_t t_parse = 0, t_timing = 0, t_apply = 0, t_events = 0, t_cal = 0,
88 t_history = 0;
90 Bool_t history_done = kFALSE;
91
95 const Bool_t skip_processing =
96 Constants::cfg.SKIP_EXISTING &&
97 FusedExists(FileSet::EventsName(spec) + ".root");
98
99 if (skip_processing) {
100 std::lock_guard<std::mutex> lock(fused_log_mutex);
101 Constants::Detail() << "[skip-build] " << file_label
102 << " events exist; re-making plots only" << std::endl;
103 } else {
104 TString bin_path;
106 bin_path = FileSet::SolBinPath(spec);
107 } else {
108 bin_path = FileSet::CompassBinPath(spec);
109 }
110
111 if (gSystem->AccessPathName(bin_path)) {
112 std::lock_guard<std::mutex> lock(fused_log_mutex);
113 std::cerr << "[fail] " << file_label
114 << (Constants::ActiveUseSolarisData() ? " SOL" : " BIN")
115 << " missing: " << bin_path << std::endl;
116 return kFALSE;
117 }
118
119 PrintMemUsage((TString("before binary read ") + file_label).Data());
120
121 t0 = std::chrono::steady_clock::now();
122 std::vector<RawHit> hits;
123
125 // SOLARIS: stream blocks directly to RawHit (no intermediate SOLHit
126 // vector)
127 SOLReader sol_reader;
128 sol_reader.SetSkipTraces(kTRUE);
129 if (!sol_reader.Open(bin_path.Data())) {
130 std::lock_guard<std::mutex> lock(fused_log_mutex);
131 std::cerr << "[fail] " << file_label << " cannot open SOL file"
132 << std::endl;
133 return kFALSE;
134 }
135 // Reserve an estimate to avoid repeated reallocation (~1M blocks typical)
136 hits.reserve(1048576);
137 while (sol_reader.ReadEvent())
138 hits.push_back(SOLHitToRawHit(sol_reader.GetCurrentEvent()));
139 sol_reader.Close();
140 } else {
141 // CoMPASS: direct conversion to RawHit
142 UShort_t use_header = (spec.suffix == "") ? 0 : run_header;
143 std::pair<std::vector<RawHit>, UShort_t> parsed =
144 InitUtils::ConvertCoMPASSBinToHits(bin_path, use_header);
145 hits = parsed.first;
146 if (spec.suffix == "")
147 BinaryToRoot::WriteHeaderFile(spec.run, parsed.second);
148 }
149 t_parse = FusedSecSince(t0);
150
151 PrintMemUsage((TString("after binary read ") + file_label).Data());
152
153 if (hits.empty()) {
154 std::lock_guard<std::mutex> lock(fused_log_mutex);
155 std::cerr << "[fail] " << file_label << " parse produced no hits"
156 << std::endl;
157 return kFALSE;
158 }
159
161 t0 = std::chrono::steady_clock::now();
163 hits, file_label, Constants::ActiveTimingRefBoard(),
165 Constants::cfg.TIMING_MIN_ENERGY, Constants::cfg.TIMING_MAX_ENERGY,
166 Constants::cfg.TIMING_OVERLAP_MARGIN_S,
167 Constants::cfg.TIMING_THRESH_DT_US);
168 t_timing = FusedSecSince(t0);
169
170 PrintMemUsage((TString("after timing ") + file_label).Data());
171
172 t0 = std::chrono::steady_clock::now();
173 Timing::ApplyShiftsInPlace(hits, shift_result.board_shifts);
176 t_apply = FusedSecSince(t0);
177
178 PrintMemUsage((TString("after apply+sort ") + file_label).Data());
179 } else if (Constants::FileInSample()) {
180 std::lock_guard<std::mutex> lock(fused_log_mutex);
181 std::cout << "[skip-timing] " << file_label
182 << " board sync and sort both disabled; skipping to event "
183 "build"
184 << std::endl;
185 }
186
187 // Pole-zero pulse-history correction before build, changing energies it
188 // dedups on; the groups are the active epoch's, none on skips it.
189 if (Constants::cfg.PULSE_HISTORY_CORRECTION &&
191 t0 = std::chrono::steady_clock::now();
192 std::vector<Int_t> groups = PulseHistory::BuildGroupMap();
193 if (PulseHistory::Measure(hits, groups, history, file_label))
194 PulseHistory::Apply(hits, groups, history);
195 PulseHistory::SavePlots(history, file_label);
196 history_done = kTRUE;
197 t_history = FusedSecSince(t0);
198 std::lock_guard<std::mutex> lock(fused_log_mutex);
199 std::cout << PulseHistory::Report(history, file_label,
201 }
202
203 t0 = std::chrono::steady_clock::now();
205 hits, slot_map, FileSet::EventsName(spec), file_label);
206 t_events = FusedSecSince(t0);
207
208 PrintMemUsage((TString("after event build ") + file_label).Data());
209
210 std::vector<RawHit>().swap(hits);
211
212 PrintMemUsage((TString("after hits free ") + file_label).Data());
213
214 if (!build_ok) {
215 std::lock_guard<std::mutex> lock(fused_log_mutex);
216 std::cerr << "[fail] " << file_label << " event build failed"
217 << std::endl;
218 return kFALSE;
219 }
220 if (history_done)
222 history);
223 }
224
225 // Calibration reads events file, fresh or pre-existing, and fits beam
226 // peaks for gains; runs plot-only too, regenerating diagnostic histograms.
227 {
228 t0 = std::chrono::steady_clock::now();
230 t_cal = FusedSecSince(t0);
231 PrintMemUsage((TString("after calibration ") + file_label).Data());
232 }
233
234 // Normed summary histograms (requires calibration from above).
235 {
236 t0 = std::chrono::steady_clock::now();
238 file_label);
239 Double_t t_normed = FusedSecSince(t0);
241 std::lock_guard<std::mutex> lock(fused_log_mutex);
242 std::cout << " normed summary: " << t_normed << "s" << std::endl;
243 }
244 }
245
246 Double_t total = FusedSecSince(t_total);
247 {
248 std::lock_guard<std::mutex> lock(fused_log_mutex);
249 std::cout << std::fixed << std::setprecision(1) << "[done] " << file_label
250 << " total=" << total << "s parse=" << t_parse
251 << " timing=" << t_timing << " apply=" << t_apply
252 << " history=" << t_history << " events=" << t_events
253 << " cal=" << t_cal << std::endl;
254 }
255 return kTRUE;
256}
257
262static void RunActiveSelection() {
263 std::vector<FileSpec> specs = FileSet::BuildRawOrProcessedFileSpecs();
264 Int_t n_specs = Int_t(specs.size());
265
266 std::set<Int_t> unique_runs;
267 for (Int_t k = 0; k < n_specs; k++)
268 unique_runs.insert(specs[k].run);
269
273 std::set<Int_t> runs_needing_header;
274 for (Int_t k = 0; k < n_specs; k++) {
275 Bool_t will_build = !(Constants::cfg.SKIP_EXISTING &&
276 FusedExists(FileSet::EventsName(specs[k]) + ".root"));
277 if (will_build)
278 runs_needing_header.insert(specs[k].run);
279 }
280
281 std::cout << "Phase A: gathering global headers for "
282 << runs_needing_header.size() << " run(s)..." << std::endl;
283 std::map<Int_t, UShort_t> run_headers;
284 for (std::set<Int_t>::const_iterator it = runs_needing_header.begin();
285 it != runs_needing_header.end(); ++it) {
286 UShort_t h;
287 if (!EnsureRunHeaderFused(*it, h)) {
288 std::cerr << "Header gather FAILED for run " << *it << std::endl;
289 continue;
290 }
291 run_headers[*it] = h;
292 std::cout << " Run " << *it << " header 0x" << std::hex << h << std::dec
293 << std::endl;
294 }
295
297
298 std::vector<ChannelCal> chans = CalibrateBeam::BuildChannels();
299
300 Int_t n_workers =
301 TMath::Min(Int_t(std::thread::hardware_concurrency()), n_specs);
302 n_workers = TMath::Min(n_workers, Constants::cfg.MAX_FUSED_WORKERS);
303 std::cout << "Phase B: fused pipeline on " << n_specs << " files with "
304 << n_workers << " workers." << std::endl;
305
306 std::queue<Int_t> work;
307 for (Int_t k = 0; k < n_specs; k++)
308 work.push(k);
309 std::mutex work_mutex;
310
311 std::vector<std::thread> workers;
312 for (Int_t w = 0; w < n_workers; w++) {
313 workers.emplace_back([&]() {
314 while (true) {
315 Int_t k;
316 {
317 std::lock_guard<std::mutex> lk(work_mutex);
318 if (work.empty())
319 return;
320 k = work.front();
321 work.pop();
322 }
323 FileSpec spec = specs[k];
324 UShort_t header =
325 run_headers.count(spec.run) ? run_headers[spec.run] : UShort_t(0);
326 // The first files of the epoch draw their per-subfile figures.
328 Bool_t ok = RunFusedPipelineForFile(spec, header, slot_map, chans);
329 if (!ok) {
330 std::lock_guard<std::mutex> lk(fused_log_mutex);
331 std::cerr << "FAILED: " << FileSet::FileLabel(spec) << std::endl;
332 }
333 }
334 });
335 }
336 for (Int_t w = 0; w < Int_t(workers.size()); w++)
337 workers[w].join();
338
339 std::cout << "All fused pipelines complete." << std::endl;
340
341 std::cout << "Phase C: per-run ridge-ratio aggregation" << std::endl;
342 for (std::set<Int_t>::const_iterator it = unique_runs.begin();
343 it != unique_runs.end(); ++it) {
344 std::vector<FileSpec> run_specs;
345 for (Int_t k = 0; k < n_specs; k++)
346 if (specs[k].run == *it)
347 run_specs.push_back(specs[k]);
349 }
350}
351
354 ROOT::EnableThreadSafety();
356 const TString project_root = Paths::DatasetDir();
357 InitUtils::SetROOTPreferences(PlotSaveFormat::kPNG,
358 Paths::ResultsDir() + "/plots",
359 Paths::ResultsDir() + "/root_files");
360
361 TString log_path = project_root + "/pipeline_fused.log";
362 std::ofstream log_file(log_path.Data());
363 std::streambuf *saved_cout = std::cout.rdbuf(log_file.rdbuf());
364 std::streambuf *saved_cerr = std::cerr.rdbuf(log_file.rdbuf());
365 Int_t saved_error_level = gErrorIgnoreLevel;
366 gErrorIgnoreLevel = kError;
367
368 if (Constants::cfg.EPOCHS.empty()) {
369 RunActiveSelection();
370 } else {
371 for (Int_t e = 0; e < Int_t(Constants::cfg.EPOCHS.size()); e++) {
372 const RunEpoch &epoch = Constants::cfg.EPOCHS[e];
373 if (!epoch.enabled || epoch.runs.empty())
374 continue;
375 std::cout << std::endl;
376 std::cout << "=== epoch " << epoch.name << " ("
377 << (epoch.source == kSOLARIS ? "SOLARIS" : "CoMPASS") << ", "
378 << epoch.runs.size() << " run(s), " << epoch.n_boards << "x"
379 << epoch.n_channels << " ch) ===" << std::endl;
381 RunActiveSelection();
383 }
384 }
385
386 std::cout.rdbuf(saved_cout);
387 std::cerr.rdbuf(saved_cerr);
388 gErrorIgnoreLevel = saved_error_level;
389 log_file.close();
390 std::cout << "Fused pipeline finished. Output logged to " << log_path
391 << std::endl;
392}
RawHit SOLHitToRawHit(const SOLData &sol)
Adapt one SOLARIS hit into the pipeline's RawHit.
Bool_t FusedExists(const TString &subpath)
Definition Pipeline.cpp:6
Double_t FusedSecSince(const std::chrono::steady_clock::time_point &t0)
Definition Pipeline.cpp:11
void PrintMemUsage(const char *label)
Definition Pipeline.cpp:18
Bool_t RunFusedPipelineForFile(FileSpec spec, UShort_t run_header, const EventBuilder::SlotMap &slot_map, const std::vector< ChannelCal > &chans)
Definition Pipeline.cpp:80
std::mutex fused_log_mutex
Definition Pipeline.cpp:4
Bool_t EnsureRunHeaderFused(Int_t run, UShort_t &header)
Definition Pipeline.cpp:30
Pole-zero pulse-history correction on the raw hit stream.
@ kSOLARIS
SOLARIS DAQ.
Definition RunEpoch.hpp:16
static void WriteHeaderFile(Int_t run, UShort_t header)
Write a run's global header to its file.
static Bool_t ReadHeaderFile(Int_t run, UShort_t &header)
Read a run's global header back from its file.
static void CalibrateBeamOneSubfile(const FileSpec &spec, const std::vector< ChannelCal > &chans_template)
Calibrate one subfile end to end.
static std::vector< ChannelCal > BuildChannels()
One ChannelCal per readout channel in the active channel map.
static void AggregateRidgeRatiosForRun(Int_t run, const std::vector< FileSpec > &specs)
Replace each subfile's ridge ratio and short-end offset with the run-level medians.
static SlotMap BuildSlotMap()
Build the board/channel to slot lookup for this dataset.
static Bool_t BuildEventsFromSortedHits(const std::vector< RawHit > &hits, const SlotMap &slot_map, const TString &output_name, const TString &file_label)
Build every event in a subfile and write them to a ROOT tree.
std::vector< Int_t > SlotMap
Board/channel to array slot lookup, indexed as BuildSlotMap() defines.
static void BuildNormedSummaryHistograms(const TString &input_filename, const TString &file_label)
Build the calibrated summary histograms for one events file.
static TString EventsName(const FileSpec &s)
Filename of the built-events ROOT file for a subfile.
Definition FileSet.cpp:292
static std::vector< TString > DiscoverSolRunSuffixes(Int_t run)
Subfile suffixes present on disk for a SOLARIS run.
Definition FileSet.cpp:144
static TString FileLabel(const FileSpec &s)
Human-readable label identifying a subfile.
Definition FileSet.cpp:299
static TString CompassBinPath(const FileSpec &s)
Path to a CoMPASS binary subfile.
Definition FileSet.cpp:6
static std::vector< FileSpec > BuildRawOrProcessedFileSpecs()
The union of raw and processed subfiles, without duplicates.
Definition FileSet.cpp:274
static TString SolBinPath(const FileSpec &s)
Path to a SOLARIS .sol subfile.
Definition FileSet.cpp:11
static Bool_t Init()
Load the GPU library and resolve the sort symbol.
Definition GpuAccel.cpp:9
static TString DatasetDir()
Absolute path to the active dataset directory, analysis/<iso>.
Definition Paths.cpp:63
static TString ResultsDir()
Absolute path to the directory receiving generated output.
Definition Paths.cpp:24
static void PrintLogo()
Print the project wordmark.
Definition Paths.cpp:31
static void Run()
Run the whole pipeline over the configured subfiles.
Definition Pipeline.cpp:352
static TimeShiftResult CalcTimeShiftsBeamMethodFromHits(const std::vector< RawHit > &hits, const TString &file_label, UShort_t ref_board, const std::vector< UShort_t > &board_channels, Double_t min_energy, Double_t max_energy, Double_t overlap_margin_s, Double_t thresh_dt_us)
Measure every board's offset against a reference board.
Definition Timing.cpp:654
static void SortHitsByTimestamp(std::vector< RawHit > &hits)
Sort hits into ascending timestamp order, in place.
Definition Timing.cpp:794
static void ApplyShiftsInPlace(std::vector< RawHit > &hits, const std::vector< Long64_t > &board_shifts)
Add the measured offsets to the hits, in place.
Definition Timing.cpp:784
UShort_t ActiveTimingRefBoard()
Board the others are timing-aligned against.
Bool_t ActiveDoBoardSync()
Whether to run the multi-board timing alignment.
Bool_t ActiveDoSort()
Whether to time-sort hits before event building.
Bool_t InPlotSample(Int_t k)
Whether the k-th file (0-based, in file order) of an epoch's list is in the plot sample.
const std::vector< UShort_t > & ActiveTimingRefBoardChannels()
Reference channel per board, for the alignment.
std::ostream & Detail()
std::cout for a file in the sample, a stream that discards otherwise: the per-file detail lines (per ...
Bool_t FileInSample()
Whether the file this thread is processing is in the per-file sample: every file with SAVE_FULL_PLOTS...
const DatasetConfig & cfg
The active dataset's configuration, flat block.
void SetPlotsThisFile(Bool_t on)
Mark the file this thread is about to process as in (or out of) the plot sample.
void SetActiveEpoch(const RunEpoch *epoch)
Set the epoch the Active*() accessors read from.
Bool_t ActiveUseSolarisData()
Whether this era's data is SOLARIS rather than CoMPASS.
const PulseHistoryGroups & ActivePulseHistoryGroups()
The pulse-history groups in force: the active epoch's set, or the flat PULSE_HISTORY_GROUPS when no e...
std::vector< Int_t > BuildGroupMap()
Group lookup for every (board, channel) under the active map.
void Apply(std::vector< RawHit > &hits, const std::vector< Int_t > &group_of, Result &res)
Apply the measured kernels to the hit stream, in place.
Bool_t Measure(std::vector< RawHit > &hits, const std::vector< Int_t > &group_of, Result &res, const TString &file_label)
Measure the kernels on this subfile's beam-like events.
void SavePlots(Result &res, const TString &file_label)
Draw and save the diagnostics, then free them.
void WriteToEventsFile(const TString &events_subpath, const Result &res)
Record the kernels and counters alongside a subfile's events.
TString Report(const Result &res, const TString &file_label, Bool_t detail=kTRUE)
Format the pass as a human-readable report.
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
Everything one subfile's pulse-history pass produced.
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
Int_t n_boards
Boards in this era's setup.
Definition RunEpoch.hpp:54
RunSource source
Acquisition system for this era.
Definition RunEpoch.hpp:36
Int_t n_channels
Channels per board.
Definition RunEpoch.hpp:55
Per-board timing offsets, indexed by board number.
Definition Timing.hpp:37
std::vector< Long64_t > board_shifts
Offset to add to each board's timestamps, in picoseconds.
Definition Timing.hpp:40