7 TString full = IO::GetRootFilesBaseDir() +
"/" + subpath;
8 return !gSystem->AccessPathName(full);
11Double_t
FusedSecSince(
const std::chrono::steady_clock::time_point &t0) {
12 return std::chrono::duration<Double_t>(std::chrono::steady_clock::now() - t0)
20 std::ifstream statm(
"/proc/self/statm");
22 statm >> dummy >> rss;
23 Double_t rss_gb = rss * 4096.0 / (1024.0 * 1024.0 * 1024.0);
25 std::cout <<
"[MEM] " << label <<
": " << rss_gb <<
" GB RSS" << std::endl;
35 Bool_t found = kFALSE;
36 for (Int_t k = 0; k < Int_t(suffixes.size()); k++) {
41 if (gSystem->AccessPathName(sol_path))
45 if (!reader.Open(sol_path.Data()))
47 if (!reader.ReadEvent()) {
51 header = reader.GetCurrentEvent().block_header;
57 std::cerr <<
"SOL header gather FAILED for run " << run
58 <<
" (no accessible files)" << std::endl;
67 std::pair<std::vector<RawHit>, UShort_t> p =
68 InitUtils::ConvertCoMPASSBinToHits(bin_path, 0);
80 const std::vector<ChannelCal> &chans) {
82 std::chrono::steady_clock::time_point t_total =
83 std::chrono::steady_clock::now();
84 std::chrono::steady_clock::time_point t0;
85 Double_t t_parse = 0, t_timing = 0, t_apply = 0, t_events = 0, t_cal = 0,
88 Bool_t history_done = kFALSE;
93 const Bool_t skip_processing =
97 if (skip_processing) {
99 std::cout <<
"[skip-build] " << file_label
100 <<
" events exist; re-making plots only" << std::endl;
109 if (gSystem->AccessPathName(bin_path)) {
111 std::cerr <<
"[fail] " << file_label
113 <<
" missing: " << bin_path << std::endl;
117 PrintMemUsage((TString(
"before binary read ") + file_label).Data());
119 t0 = std::chrono::steady_clock::now();
120 std::vector<RawHit> hits;
125 SOLReader sol_reader;
126 sol_reader.SetSkipTraces(kTRUE);
127 if (!sol_reader.Open(bin_path.Data())) {
129 std::cerr <<
"[fail] " << file_label <<
" cannot open SOL file"
134 hits.reserve(1048576);
135 while (sol_reader.ReadEvent()) {
136 const SOLData &sol = sol_reader.GetCurrentEvent();
139 raw.channel = sol.channel;
140 raw.energy = sol.energy;
141 raw.timestamp = sol.timestamp * 1000;
148 UShort_t use_header = (spec.
suffix ==
"") ? 0 : run_header;
149 std::pair<std::vector<RawHit>, UShort_t> parsed =
150 InitUtils::ConvertCoMPASSBinToHits(bin_path, use_header);
157 PrintMemUsage((TString(
"after binary read ") + file_label).Data());
161 std::cerr <<
"[fail] " << file_label <<
" parse produced no hits"
167 t0 = std::chrono::steady_clock::now();
176 PrintMemUsage((TString(
"after timing ") + file_label).Data());
178 t0 = std::chrono::steady_clock::now();
184 PrintMemUsage((TString(
"after apply+sort ") + file_label).Data());
187 std::cout <<
"[skip-timing] " << file_label
188 <<
" board sync and sort both disabled; skipping to event "
197 t0 = std::chrono::steady_clock::now();
202 history_done = kTRUE;
208 t0 = std::chrono::steady_clock::now();
213 PrintMemUsage((TString(
"after event build ") + file_label).Data());
215 std::vector<RawHit>().swap(hits);
217 PrintMemUsage((TString(
"after hits free ") + file_label).Data());
221 std::cerr <<
"[fail] " << file_label <<
" event build failed"
233 if (!chans.empty()) {
234 t0 = std::chrono::steady_clock::now();
237 PrintMemUsage((TString(
"after calibration ") + file_label).Data());
242 t0 = std::chrono::steady_clock::now();
247 std::cout <<
" normed summary: " << t_normed <<
"s" << std::endl;
253 std::cout << std::fixed << std::setprecision(1) <<
"[done] " << file_label
254 <<
" total=" << total <<
"s parse=" << t_parse
255 <<
" timing=" << t_timing <<
" apply=" << t_apply
256 <<
" history=" << t_history <<
" events=" << t_events
257 <<
" cal=" << t_cal << std::endl;
266static void RunActiveSelection() {
268 Int_t n_specs = Int_t(specs.size());
270 std::set<Int_t> unique_runs;
271 for (Int_t k = 0; k < n_specs; k++)
272 unique_runs.insert(specs[k].run);
277 std::set<Int_t> runs_needing_header;
278 for (Int_t k = 0; k < n_specs; k++) {
282 runs_needing_header.insert(specs[k].run);
285 std::cout <<
"Phase A: gathering global headers for "
286 << runs_needing_header.size() <<
" run(s)..." << std::endl;
287 std::map<Int_t, UShort_t> run_headers;
288 for (std::set<Int_t>::const_iterator it = runs_needing_header.begin();
289 it != runs_needing_header.end(); ++it) {
292 std::cerr <<
"Header gather FAILED for run " << *it << std::endl;
295 run_headers[*it] = h;
296 std::cout <<
" Run " << *it <<
" header 0x" << std::hex << h << std::dec
302 std::vector<ChannelCal> chans;
304 std::cout <<
"SKIP_CALIBRATION=true; skipping beam calibration and eres "
305 "aggregation (events kept in raw ADC)."
312 TMath::Min(Int_t(std::thread::hardware_concurrency()), n_specs);
313 n_workers = TMath::Min(n_workers,
Constants::cfg.MAX_FUSED_WORKERS);
314 std::cout <<
"Phase B: fused pipeline on " << n_specs <<
" files with "
315 << n_workers <<
" workers." << std::endl;
317 std::queue<Int_t> work;
318 for (Int_t k = 0; k < n_specs; k++)
320 std::mutex work_mutex;
322 std::vector<std::thread> workers;
323 for (Int_t w = 0; w < n_workers; w++) {
324 workers.emplace_back([&]() {
328 std::lock_guard<std::mutex> lk(work_mutex);
336 run_headers.count(spec.
run) ? run_headers[spec.
run] : UShort_t(0);
345 for (Int_t w = 0; w < Int_t(workers.size()); w++)
348 std::cout <<
"All fused pipelines complete." << std::endl;
350 if (!chans.empty()) {
351 std::cout <<
"Phase C: per-run ridge-ratio aggregation" << std::endl;
352 for (std::set<Int_t>::const_iterator it = unique_runs.begin();
353 it != unique_runs.end(); ++it) {
354 std::vector<FileSpec> run_specs;
355 for (Int_t k = 0; k < n_specs; k++)
356 if (specs[k].run == *it)
357 run_specs.push_back(specs[k]);
360 std::cout <<
"Phase C: per-run eres TOML aggregation" << std::endl;
361 for (std::set<Int_t>::const_iterator it = unique_runs.begin();
362 it != unique_runs.end(); ++it) {
363 std::vector<FileSpec> run_specs;
364 for (Int_t k = 0; k < n_specs; k++)
365 if (specs[k].run == *it)
366 run_specs.push_back(specs[k]);
374 ROOT::EnableThreadSafety();
377 InitUtils::SetROOTPreferences(PlotSaveFormat::kPNG,
381 TString log_path = project_root +
"/pipeline_fused.log";
382 std::ofstream log_file(log_path.Data());
383 std::streambuf *saved_cout = std::cout.rdbuf(log_file.rdbuf());
384 std::streambuf *saved_cerr = std::cerr.rdbuf(log_file.rdbuf());
385 Int_t saved_error_level = gErrorIgnoreLevel;
386 gErrorIgnoreLevel = kError;
389 RunActiveSelection();
395 std::cout << std::endl;
396 std::cout <<
"=== epoch " << epoch.
name <<
" ("
398 << epoch.
runs.size() <<
" run(s), " << epoch.
n_boards <<
"x"
399 << epoch.
n_channels <<
" ch) ===" << std::endl;
401 RunActiveSelection();
406 std::cout.rdbuf(saved_cout);
407 std::cerr.rdbuf(saved_cerr);
408 gErrorIgnoreLevel = saved_error_level;
410 std::cout <<
"Fused pipeline finished. Output logged to " << log_path
UInt_t MapSOLFlagsToCoMPASS(UShort_t sol_flags_high, UShort_t sol_flags_low)
Pack SOLARIS flags into a CoMPASS-compatible word.
Bool_t FusedExists(const TString &subpath)
Double_t FusedSecSince(const std::chrono::steady_clock::time_point &t0)
void PrintMemUsage(const char *label)
Bool_t RunFusedPipelineForFile(FileSpec spec, UShort_t run_header, const EventBuilder::SlotMap &slot_map, const std::vector< ChannelCal > &chans)
std::mutex fused_log_mutex
Bool_t EnsureRunHeaderFused(Int_t run, UShort_t &header)
Pole-zero pulse-history correction on the raw hit stream.
static Bool_t ReadHeaderSidecar(Int_t run, UShort_t &header)
Read a run's global header back from its sidecar.
static void WriteHeaderSidecar(Int_t run, UShort_t header)
Write a run's global header to its sidecar.
static void CalibrateBeamOneSubfile(const FileSpec &spec, const std::vector< ChannelCal > &chans_template)
Calibrate one subfile end to end.
static void AggregateEresTomlForRun(Int_t run, const std::vector< FileSpec > &specs)
Aggregate the run's energy-resolution measurements into its TOML.
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 with the run-level median.
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.
static std::vector< TString > DiscoverSolRunSuffixes(Int_t run)
Subfile suffixes present on disk for a SOLARIS run.
static TString FileLabel(const FileSpec &s)
Human-readable label identifying a subfile.
static TString CompassBinPath(const FileSpec &s)
Path to a CoMPASS binary subfile.
static std::vector< FileSpec > BuildRawOrProcessedFileSpecs()
The union of raw and processed subfiles, without duplicates.
static TString SolBinPath(const FileSpec &s)
Path to a SOLARIS .sol subfile.
static Bool_t Init()
Load the GPU library and resolve the sort symbol.
static TString DatasetDir()
Absolute path to the active dataset directory, analysis/<iso>.
static TString ResultsDir()
Absolute path to the directory receiving generated output.
static void PrintLogo()
Print the project wordmark.
static void Run()
Run the whole pipeline over the configured subfiles.
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.
static void SortHitsByTimestamp(std::vector< RawHit > &hits)
Sort hits into ascending timestamp order, in place.
static void ApplyShiftsInPlace(std::vector< RawHit > &hits, const std::vector< Long64_t > &board_shifts)
Add the measured offsets to the hits, in place.
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.
const std::vector< UShort_t > & ActiveTimingRefBoardChannels()
Reference channel per board, for the alignment.
const DatasetConfig & cfg
The active dataset's configuration, flat block.
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.
TString Report(const Result &res, const TString &file_label)
Format the pass as a human-readable report.
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.
One input file: a run number and the subfile suffix within it.
TString suffix
Subfile suffix, empty for the first subfile.
Everything one subfile's pulse-history pass produced.
One acquisition period of a dataset.
std::vector< Int_t > runs
Run numbers belonging to this epoch.
TString name
Epoch name, used in logs and plot paths.
Bool_t enabled
Whether this epoch participates in the analysis.
Int_t n_boards
Boards in this era's setup.
RunSource source
Acquisition system for this era.
Int_t n_channels
Channels per board.
Per-board timing offsets, indexed by board number.
std::vector< Long64_t > board_shifts
Offset to add to each board's timestamps, in picoseconds.