167 UShort_t *left_0_17_branch, UShort_t *rightdE_branch,
168 UShort_t *hits_branch, Short_t &cathode_branch,
169 Short_t &grid_branch, UInt_t &flags_or_branch,
172 std::vector<TGraph *> *sample_traces =
nullptr,
173 Long64_t sample_stride = 0, Int_t *n_sampled =
nullptr) {
174 for (Int_t s = 1; s < 17; s++)
178 if (sample_traces && event_idx >= 0 && sample_stride > 0 &&
179 event_idx % sample_stride == 0 &&
180 Int_t(sample_traces->size()) <
Constants::cfg.SAVE_SAMPLE_TRACES) {
182 for (Int_t s = 0; s < 18; s++)
183 total[s] = Double_t(e.
totaldE[s]);
191 if (e.
hits[34] > 1) {
195 Bool_t any_anode_multi = kFALSE;
196 for (Int_t k = 0; k < 34; k++) {
198 any_anode_multi = kTRUE;
214 for (Int_t s = 1; s <= 16; s++) {
220 Bool_t has_fake = kFALSE, has_saturation = kFALSE, has_pileup = kFALSE;
222 Bool_t has_any_flag = has_fake || has_saturation || has_pileup;
225 Bool_t reject =
Constants::cfg.REJECT_FLAGGED_EVENTS && has_any_flag;
229 for (Int_t k = 0; k < 18; k++) {
230 left_0_17_branch[k] = UShort_t(e.
leftdE[k]);
231 rightdE_branch[k] = UShort_t(e.
rightdE[k]);
236 left_0_17_branch[0] = UShort_t(e.
totaldE[0]);
237 left_0_17_branch[17] = UShort_t(e.
totaldE[17]);
239 hits_branch[k] = UShort_t(e.
hits[k]);
240 cathode_branch = Short_t(e.
cathode);
241 grid_branch = Short_t(e.
grid);
243 seed_ts_branch = e.
ref_ts;
246 for (Int_t s = 0; s < 18; s++)
249 for (Int_t s = 1; s <= 16; s++) {
250 const Bool_t lIsLong = (s % 2) != 0;
274 hSum.
h_mult->Fill(Double_t(mult));
298 const TString &output_name,
299 const TString &file_label) {
300 TString output_filepath = output_name +
".root";
301 TFile *output_file = IO::OpenForWriting(output_filepath);
302 if (!output_file || output_file->IsZombie()) {
303 std::cerr <<
"Error opening output: " << output_filepath << std::endl;
314 UShort_t left_0_17_dE[18], rightdE[18];
315 UShort_t hits_arr[36];
320 Short_t cathode, grid;
324 TTree *output_tree =
new TTree(
"events",
"MUSIC events");
325 output_tree->Branch(
"Left_0_17_dE", left_0_17_dE,
"Left_0_17_dE[18]/s");
326 output_tree->Branch(
"RightdE", rightdE,
"RightdE[18]/s");
327 output_tree->Branch(
"Hits", hits_arr,
"Hits[36]/s");
328 output_tree->Branch(
"Cathode", &cathode,
"Cathode/S");
329 output_tree->Branch(
"Grid", &grid,
"Grid/S");
330 output_tree->Branch(
"FlagsOR", &flags_or,
"FlagsOR/i");
331 output_tree->Branch(
"SeedTs", &seed_ts,
"SeedTs/l");
335 output_tree->SetAutoFlush(0);
336 for (Int_t b = 0; b < output_tree->GetListOfBranches()->GetEntries(); b++) {
338 static_cast<TBranch *
>(output_tree->GetListOfBranches()->At(b));
339 branch->SetBasketSize(128 * 1024 * 1024);
343 cfg.unit_label =
"ADC";
346 cfg.odd_even_split = kTRUE;
355 cfg.music_energy_bins = 2000;
361 std::vector<TGraph *> sample_traces;
362 Long64_t sample_stride = 0;
367 Long64_t est_events = Long64_t(hits.size()) / 20;
368 sample_stride = est_events / Long64_t(
Constants::cfg.SAVE_SAMPLE_TRACES);
369 if (sample_stride < 1)
379 for (std::map<std::pair<Int_t, Int_t>, TString>::const_iterator it =
383 Int_t board = it->first.first;
384 Int_t channel = it->first.second;
393 std::cerr <<
"FATAL: reference channel '"
395 <<
"' not found in channel map. Cannot build events."
397 output_file->Close();
399 for (Int_t s = 1; s <= 16; s++)
408 for (Int_t i = 0; i < Int_t(sample_traces.size()); i++)
409 delete sample_traces[i];
414 Long64_t n_entries = Long64_t(hits.size());
425 ULong64_t cur_ref_ts = 0;
426 Bool_t have_cur = kFALSE;
428 std::vector<PendingHit> pending;
429 pending.reserve(4096);
447 for (Int_t s = 0; s < 17; s++)
468 ULong64_t first_ref_ts = 0;
469 ULong64_t last_ref_ts = 0;
470 Int_t emptyChannelMapEvents = 0;
471 Long64_t cathode_hits_total = 0;
472 Long64_t dropped_outside_window = 0;
473 Long64_t event_idx = 0;
479 std::cout <<
"[" << file_label <<
"] Streaming pass over " << n_entries
480 <<
" sorted hits (reference: "
485 for (Long64_t i = 0; i < n_entries; i++) {
486 const RawHit &h = hits[i];
490 emptyChannelMapEvents++;
495 emptyChannelMapEvents++;
500 cathode_hits_total++;
506 if (slot == ref_slot) {
514 first_ref_ts = h.timestamp;
515 last_ref_ts = h.timestamp;
520 for (Int_t p = 0; p < Int_t(pending.size()); p++) {
521 if (pending[p].timestamp - cur_ref_ts <= window_ps) {
522 AssignHit(cur_event, pc_cur, cur_ref_ts, pending[p].slot,
523 pending[p].energy, pending[p].timestamp,
524 pending[p].flags, dedup_strat);
526 dropped_outside_window++;
532 FinalizeEvent(cur_event, pc_cur, output_tree, left_0_17_dE, rightdE,
533 hits_arr, cathode, grid, flags_or, seed_ts, hSum, cnt,
534 event_idx, &sample_traces, sample_stride, &n_sampled);
546 cur_ref_ts = h.timestamp;
547 cur_event.
ref_ts = cur_ref_ts;
549 AssignHit(cur_event, pc_cur, cur_ref_ts, ref_slot, h.energy,
550 h.timestamp, h.
flags, dedup_strat);
552 if (have_cur && h.timestamp - cur_ref_ts <= window_ps) {
555 ph.energy = h.energy;
556 ph.timestamp = h.timestamp;
558 pending.push_back(ph);
559 }
else if (have_cur) {
560 dropped_outside_window++;
570 cur_ref_ts = h.timestamp;
571 cur_event.
ref_ts = cur_ref_ts;
573 AssignHit(cur_event, pc_cur, cur_ref_ts, slot, h.energy, h.timestamp,
574 h.
flags, dedup_strat);
575 }
else if (h.timestamp - cur_ref_ts <= window_ps) {
576 AssignHit(cur_event, pc_cur, cur_ref_ts, slot, h.energy, h.timestamp,
577 h.
flags, dedup_strat);
580 FinalizeEvent(cur_event, pc_cur, output_tree, left_0_17_dE, rightdE,
581 hits_arr, cathode, grid, flags_or, seed_ts, hSum, cnt,
582 event_idx, &sample_traces, sample_stride, &n_sampled);
586 cur_ref_ts = h.timestamp;
587 cur_event.
ref_ts = cur_ref_ts;
588 AssignHit(cur_event, pc_cur, cur_ref_ts, slot, h.energy, h.timestamp,
589 h.
flags, dedup_strat);
593 if (i % 10000000 == 0)
594 std::cout <<
" Stream progress: " << i <<
"/" << n_entries << std::endl;
600 for (Int_t p = 0; p < Int_t(pending.size()); p++) {
601 if (pending[p].timestamp - cur_ref_ts <= window_ps) {
602 AssignHit(cur_event, pc_cur, cur_ref_ts, pending[p].slot,
603 pending[p].energy, pending[p].timestamp, pending[p].flags,
606 dropped_outside_window++;
611 FinalizeEvent(cur_event, pc_cur, output_tree, left_0_17_dE, rightdE,
612 hits_arr, cathode, grid, flags_or, seed_ts, hSum, cnt,
613 event_idx, &sample_traces, sample_stride, &n_sampled);
618 <<
" hits." << std::endl;
620 if (ref_mode && n_ref == 0) {
622 <<
" hits in file, skipping." << std::endl;
623 output_file->Close();
625 for (Int_t s = 1; s <= 16; s++)
637 Double_t span_s = (last_ref_ts > first_ref_ts)
638 ? Double_t(last_ref_ts - first_ref_ts) / 1e12
640 Double_t ref_rate_hz = (span_s > 0.0) ? Double_t(n_ref) / span_s : 0.0;
643 TParameter<Double_t>(
"grid_rate_hz", ref_rate_hz).Write();
644 output_tree->Write(
"events", TObject::kOverwrite);
645 hSum.
h_mult->Write(
"", TObject::kOverwrite);
649 TString subdir =
"events_summary/" + file_label;
653 if (!sample_traces.empty()) {
660 output_file->Close();
663 if (emptyChannelMapEvents != 0)
664 std::cout <<
"Observed " << emptyChannelMapEvents
665 <<
" hits with empty entry in channel map." << std::endl;
667 if (dropped_outside_window > 0)
668 std::cout <<
"Dropped " << dropped_outside_window
669 <<
" hits outside coincidence window." << std::endl;
674 <<
" trigger rate: " << ref_rate_hz <<
" Hz over " << span_s <<
" s"
676 std::cout <<
"Cathode hits total: " << cathode_hits_total <<
" (cathode/"
678 << (n_ref > 0 ? Double_t(cathode_hits_total) / n_ref : 0.0) <<
")"
680 std::cout <<
"Total events: " << total_events << std::endl;
681 if (total_events > 0) {
682 std::cout <<
"Events with cathode hit: " << events_with_cathode <<
" ("
683 << (100.0 * events_with_cathode / total_events) <<
"%)"
685 std::cout <<
"Events with multiple cathode hits: "
686 << events_with_multi_cathode <<
" ("
687 << (100.0 * events_with_multi_cathode / total_events)
689 << (events_with_cathode > 0
690 ? 100.0 * events_with_multi_cathode / events_with_cathode
692 <<
"% of cathode events)" << std::endl;
693 std::cout <<
"Events with multi-hit on any anode: "
694 << events_with_multi_anode_hit <<
" ("
695 << (100.0 * events_with_multi_anode_hit / total_events) <<
"%)"
697 std::cout <<
"Dropped hits (dedup strategy): anode="
698 << dropped_anode_hits_total
699 <<
", cathode=" << dropped_cathode_hits_total << std::endl;
701 std::cout <<
"Complete events: " << complete_events <<
" ("
702 << (100.0 * complete_events / total_events) <<
"%)" << std::endl;
703 std::cout <<
"Incomplete events: " << incomplete_events <<
" ("
704 << (100.0 * incomplete_events / total_events) <<
"%)" << std::endl;
705 if (total_events > 0) {
706 std::cout <<
"Per-channel miss rate (% of all events where the "
707 "completeness-required channel was zero):"
709 std::cout <<
" Strip0=" << (100.0 * cnt.
miss_strip0 / total_events)
710 <<
"% Strip17=" << (100.0 * cnt.
miss_strip17 / total_events)
712 for (Int_t s = 1; s <= 16; s++) {
713 const Char_t *side = (s % 2 == 1) ?
"L" :
"R";
714 std::cout <<
" " << side << s <<
"="
715 << (100.0 * cnt.
miss_long[s] / total_events) <<
"%";
717 std::cout << std::endl;
721 Int_t stored = complete_events - complete_rejected;
722 std::cout <<
"Stored events (REJECT_FLAGGED_EVENTS=true): " << stored
724 << (complete_events > 0 ? 100.0 * stored / complete_events : 0.0)
725 <<
"% of complete; " << complete_rejected <<
" rejected)"
728 if (complete_events > 0) {
729 std::cout <<
"Complete events with rejection-quality flags:" << std::endl;
730 std::cout <<
" Fake events: " << complete_with_fake <<
" ("
731 << (100.0 * complete_with_fake / complete_events) <<
"%)"
733 std::cout <<
" Saturated: " << complete_with_saturation <<
" ("
734 << (100.0 * complete_with_saturation / complete_events) <<
"%)"
736 std::cout <<
" Pileup: " << complete_with_pileup <<
" ("
737 << (100.0 * complete_with_pileup / complete_events) <<
"%)"
740 if (incomplete_events > 0) {
741 std::cout <<
"Incomplete events with rejection-quality flags:" << std::endl;
742 std::cout <<
" Fake events: " << incomplete_with_fake <<
" ("
743 << (100.0 * incomplete_with_fake / incomplete_events) <<
"%)"
745 std::cout <<
" Saturated: " << incomplete_with_saturation <<
" ("
746 << (100.0 * incomplete_with_saturation / incomplete_events)
747 <<
"%)" << std::endl;
748 std::cout <<
" Pileup: " << incomplete_with_pileup <<
" ("
749 << (100.0 * incomplete_with_pileup / incomplete_events) <<
"%)"
753 std::cout <<
"Events saved to: " << output_filepath << std::endl;