5 for (Int_t k = 0; k < 16; k++) {
29 UShort_t cand_energy, UShort_t prev_energy,
32 return cand_energy < prev_energy;
34 return cand_energy > prev_energy;
36 return cand_ts > prev_ts;
38 ULong64_t cand_dt = (cand_ts > ref_ts) ? cand_ts - ref_ts : ref_ts - cand_ts;
39 ULong64_t prev_dt = (prev_ts > ref_ts) ? prev_ts - ref_ts : ref_ts - prev_ts;
40 return cand_dt < prev_dt;
46 for (std::map<std::pair<Int_t, Int_t>, TString>::const_iterator it =
49 Int_t board = it->first.first;
50 Int_t channel = it->first.second;
56 const TString &name = it->second;
57 if (name ==
"Strip0") {
59 }
else if (name ==
"Strip17") {
61 }
else if (name ==
"Cathode") {
63 }
else if (name ==
"Grid") {
65 }
else if (name.Length() >= 2 && (name[0] ==
'L' || name[0] ==
'R')) {
66 TString numstr = name(1, name.Length() - 1);
67 if (numstr.IsDigit()) {
68 Int_t s = numstr.Atoi();
69 if (s >= 1 && s <= 16)
70 sm[idx] = (name[0] ==
'L') ? s : (s + 16);
78 ULong64_t ref_ts, Int_t slot, UShort_t energy,
79 ULong64_t timestamp, UInt_t flags,
88 if (e.
hits[slot] == 0) {
92 pc->
energies[slot], ref_ts, strategy);
102 e.
leftdE[slot - 1] = energy;
114 pc->
flags[slot] = flags;
130 for (Int_t strip = 1; strip <= 16; strip++) {
132 (strip % 2) != 0 ? e.
leftdE[strip - 1] : e.
rightdE[strip - 1];
140 Bool_t &has_saturation, Bool_t &has_pileup) {
141 has_fake = (e.
flags_or & CoMPASSData::FAKE_EVENT) != 0;
142 has_saturation = ((e.
flags_or & CoMPASSData::SATURATION_IN_GATE) ||
143 (e.
flags_or & CoMPASSData::INPUT_SATURATING)) != 0;
144 has_pileup = (e.
flags_or & CoMPASSData::PILEUP) != 0;
172 UShort_t *rightdE_branch, UShort_t &strip0_branch,
173 UShort_t &strip17_branch, UShort_t *hits_branch,
174 Short_t &cathode_branch, Short_t &grid_branch,
175 UInt_t &flags_or_branch, ULong64_t &seed_ts_branch,
177 Long64_t event_idx = -1,
178 std::vector<TGraph *> *sample_traces =
nullptr,
179 Long64_t sample_stride = 0, Int_t *n_sampled =
nullptr) {
181 if (sample_traces && event_idx >= 0 && sample_stride > 0 &&
182 event_idx % sample_stride == 0 &&
183 Int_t(sample_traces->size()) <
Constants::cfg.SAVE_SAMPLE_TRACES) {
185 for (Int_t s = 0; s < 18; s++)
186 total[s] = Double_t(e.
Total(s));
194 if (e.
hits[34] > 1) {
198 Bool_t any_anode_multi = kFALSE;
199 for (Int_t k = 0; k < 34; k++) {
201 any_anode_multi = kTRUE;
217 for (Int_t s = 1; s <= 16; s++) {
223 Bool_t has_fake = kFALSE, has_saturation = kFALSE, has_pileup = kFALSE;
225 Bool_t has_any_flag = has_fake || has_saturation || has_pileup;
228 Bool_t reject =
Constants::cfg.REJECT_FLAGGED_EVENTS && has_any_flag;
232 for (Int_t k = 0; k < 16; k++) {
233 leftdE_branch[k] = UShort_t(e.
leftdE[k]);
234 rightdE_branch[k] = UShort_t(e.
rightdE[k]);
236 strip0_branch = UShort_t(e.
strip0dE);
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;
276 hSum.
h_mult->Fill(Double_t(mult));
383static const Int_t kPreTriggerMaxPending = 2;
388 TTree *output_tree =
new TTree(
"events",
"MUSIC events");
389 output_tree->Branch(
"LeftdE", br.
leftdE,
"LeftdE[16]/s");
390 output_tree->Branch(
"RightdE", br.
rightdE,
"RightdE[16]/s");
391 output_tree->Branch(
"Strip0dE", &br.
strip0dE,
"Strip0dE/s");
392 output_tree->Branch(
"Strip17dE", &br.
strip17dE,
"Strip17dE/s");
393 output_tree->Branch(
"Hits", br.
hits_arr,
"Hits[36]/s");
394 output_tree->Branch(
"Cathode", &br.
cathode,
"Cathode/S");
395 output_tree->Branch(
"Grid", &br.
grid,
"Grid/S");
396 output_tree->Branch(
"FlagsOR", &br.
flags_or,
"FlagsOR/i");
397 output_tree->Branch(
"SeedTs", &br.
seed_ts,
"SeedTs/l");
399 output_tree->SetAutoFlush(0);
400 for (Int_t b = 0; b < output_tree->GetListOfBranches()->GetEntries(); b++) {
402 static_cast<TBranch *
>(output_tree->GetListOfBranches()->At(b));
403 branch->SetBasketSize(128 * 1024 * 1024);
411 cfg.unit_label =
"ADC";
414 cfg.odd_even_split = kTRUE;
422 cfg.music_energy_bins = 2000;
428 for (Int_t s = 1; s <= 16; s++)
441static Long64_t SampleTraceStride(Long64_t n_hits) {
442 Long64_t sample_stride = 0;
444 Long64_t est_events = n_hits / 20;
445 sample_stride = est_events / Long64_t(
Constants::cfg.SAVE_SAMPLE_TRACES);
446 if (sample_stride < 1)
449 return sample_stride;
456 for (std::map<std::pair<Int_t, Int_t>, TString>::const_iterator it =
460 Int_t board = it->first.first;
461 Int_t channel = it->first.second;
489 for (Int_t s = 0; s < 17; s++)
497static inline void OpenNewEvent(
OpenEvent &oe,
const RawHit &h, Int_t slot,
509 h.timestamp, h.flags,
cfg.dedup_strat);
515 Long64_t &dropped_outside_window) {
516 for (Int_t p = 0; p < Int_t(oe.
pending.size()); p++) {
522 dropped_outside_window++;
540static inline Bool_t MergePreTriggerSeed(
const RawHit &h, Int_t ref_slot,
544 const Bool_t empty = Int_t(oe.
pending.size()) <= kPreTriggerMaxPending;
550 Double_t(oe.
seed_energy) <
cfg.holdoff_ratio * Double_t(h.energy)) {
552 if (
cfg.holdoff_on) {
558 const UShort_t keep_e = std::max(oe.
seed_energy, h.energy);
562 h.timestamp, h.flags,
cfg.dedup_strat);
574static inline Bool_t ReferenceModeHit(
const RawHit &h, Int_t slot,
577 if (slot ==
cfg.ref_slot) {
589 if (MergePreTriggerSeed(h,
cfg.ref_slot, oe, cfg, t))
597 CloseEvent(oe, sink);
602 OpenNewEvent(oe, h,
cfg.ref_slot, cfg, kTRUE);
611 }
else if (oe.
have) {
620static inline void WindowModeHit(
const RawHit &h, Int_t slot,
624 OpenNewEvent(oe, h, slot, cfg, kFALSE);
625 }
else if (h.timestamp - oe.
ref_ts <=
cfg.window_ps) {
627 h.timestamp, h.flags,
cfg.dedup_strat);
630 CloseEvent(oe, sink);
631 OpenNewEvent(oe, h, slot, cfg, kFALSE);
636static void PrintBuildOneLiner(
const TString &file_label,
638 Double_t ref_rate_hz,
639 Long64_t dropped_outside_window) {
642 const TString line = Form(
643 "[events] %s: %lld events, %lld complete (%.1f%%), %lld %s hits at "
644 "%.0f Hz, %lld outside window, dedup dropped %lld anode",
651 std::cout << line << std::endl;
657 Double_t ref_rate_hz, Double_t holdoff_us,
658 const TString &output_filepath) {
661 <<
" hits with empty entry in channel map." << std::endl;
665 <<
" hits outside coincidence window." << std::endl;
668 const Double_t pct = 100.0 / Double_t(t.
n_ref);
669 std::cout <<
"Seed holdoff " << (
cfg.holdoff_on ?
"ON" :
"off") <<
" ("
670 << (
cfg.holdoff_on ? holdoff_us : 10.0) <<
" us"
671 << (
cfg.holdoff_on ?
"" :
" diagnostic") <<
", guard ratio "
672 <<
cfg.holdoff_ratio <<
"x): close seed pairs "
677 <<
" with no anodes pending ("
681 <<
"%). Pre-triggers (no anodes, current seed under the guard): "
685 <<
" kept apart." << std::endl;
691 <<
" trigger rate: " << ref_rate_hz <<
" Hz over " << span_s <<
" s"
697 std::cout <<
"Total events: " << cnt.
total_events << std::endl;
702 std::cout <<
"Events with multiple cathode hits: "
710 <<
"% of cathode events)" << std::endl;
711 std::cout <<
"Events with multi-hit on any anode: "
714 <<
"%)" << std::endl;
715 std::cout <<
"Dropped hits (dedup strategy): anode="
726 std::cout <<
"Per-channel miss rate (% of all events where the "
727 "completeness-required channel was zero):"
732 for (Int_t s = 1; s <= 16; s++) {
733 const Char_t *side = (s % 2 == 1) ?
"L" :
"R";
734 std::cout <<
" " << side << s <<
"="
737 std::cout << std::endl;
742 std::cout <<
"Stored events (REJECT_FLAGGED_EVENTS=true): " << stored
750 std::cout <<
"Complete events with rejection-quality flags:" << std::endl;
756 <<
"%)" << std::endl;
759 <<
"%)" << std::endl;
762 std::cout <<
"Incomplete events with rejection-quality flags:" << std::endl;
765 <<
"%)" << std::endl;
769 <<
"%)" << std::endl;
772 <<
"%)" << std::endl;
775 std::cout <<
"Events saved to: " << output_filepath << std::endl;
780 const TString &output_name,
781 const TString &file_label) {
782 TString output_filepath = output_name +
".root";
783 TFile *output_file = IO::OpenForWriting(output_filepath);
784 if (!output_file || output_file->IsZombie()) {
785 std::cerr <<
"Error opening output: " << output_filepath << std::endl;
790 TTree *output_tree = BookEventBranches(br);
793 ConfigureSummaryHistograms(hSum);
796 std::vector<TGraph *> sample_traces;
797 Long64_t sample_stride = SampleTraceStride(Long64_t(hits.size()));
804 cfg.ref_slot = cfg.ref_mode ? ResolveReferenceSlot(slot_map) : -1;
805 if (cfg.ref_mode && cfg.ref_slot < 0) {
806 std::cerr <<
"FATAL: reference channel '"
808 <<
"' not found in channel map. Cannot build events."
810 output_file->Close();
812 DeleteSummaryHistograms(hSum);
813 for (Int_t i = 0; i < Int_t(sample_traces.size()); i++)
814 delete sample_traces[i];
823 cfg.holdoff_on = holdoff_us > 0.0;
824 cfg.holdoff_ps = ULong64_t((cfg.holdoff_on ? holdoff_us : 10.0) * 1.0e6);
826 Long64_t n_entries = Long64_t(hits.size());
837 ZeroEventCounters(cnt);
853 sink.
tree = output_tree;
863 std::cout <<
"[" << file_label <<
"] Streaming pass over " << n_entries
864 <<
" sorted hits (reference: "
869 for (Long64_t i = 0; i < n_entries; i++) {
870 const RawHit &h = hits[i];
887 if (ReferenceModeHit(h, slot, cfg, oe, sink, t))
890 WindowModeHit(h, slot, cfg, oe, sink);
893#if MUSIC_HOT_PATH_LOGGING
894 if (i % 10000000 == 0)
895 std::cout <<
" Stream progress: " << i <<
"/" << n_entries << std::endl;
903 CloseEvent(oe, sink);
907 std::cout <<
"Found " << t.
n_ref <<
" "
910 if (cfg.ref_mode && t.
n_ref == 0) {
912 <<
" hits in file, skipping." << std::endl;
913 output_file->Close();
915 DeleteSummaryHistograms(hSum);
922 Double_t ref_rate_hz = (span_s > 0.0) ? Double_t(t.
n_ref) / span_s : 0.0;
925 TParameter<Double_t>(
"grid_rate_hz", ref_rate_hz).Write();
926 output_tree->Write(
"events", TObject::kOverwrite);
927 hSum.
h_mult->Write(
"", TObject::kOverwrite);
931 TString subdir =
"events_summary/" + file_label;
935 if (!sample_traces.empty()) {
942 output_file->Close();
948 PrintBuildOneLiner(file_label, cnt, t.
n_ref, ref_rate_hz,
952 PrintBuildSummary(cfg, t, cnt, span_s, ref_rate_hz, holdoff_us,
DedupStrategy
How to resolve more than one hit on the same channel within an event.
@ kDISCARD
Discard the entire event if any anode channel has a repeated hit.
@ kLATEST_TIMESTAMP
Keep the last hit to arrive.
@ kLARGEST_ENERGY
Keep the highest-energy hit.
@ kSMALLEST_ENERGY
Keep the lowest-energy hit.
void FinalizeEvent(EventState &e, TTree *output_tree, UShort_t *leftdE_branch, UShort_t *rightdE_branch, UShort_t &strip0_branch, UShort_t &strip17_branch, UShort_t *hits_branch, Short_t &cathode_branch, Short_t &grid_branch, UInt_t &flags_or_branch, ULong64_t &seed_ts_branch, SummaryHistograms &hSum, EventCounters &c, Long64_t event_idx=-1, std::vector< TGraph * > *sample_traces=nullptr, Long64_t sample_stride=0, Int_t *n_sampled=nullptr)
Bool_t SummaryGridPartnerIsStrip1()
Whether the grid's partner in the summary is strip 1 rather than strip 0: when strip 0 is ignored or ...
void CreateSummaryHistograms(SummaryHistograms &h, const SummaryHistConfig &cfg)
Allocate every histogram in the set.
void SaveAndDeleteSummaryHistograms(SummaryHistograms &h, TFile *out_file, const TString &subdir, const TString &plot_suffix)
Write the set to file, save the plots, then free every histogram.
std::mutex g_plot_mutex
Serialises all plotting and canvas work.
static Bool_t CheckEventComplete(const EventState &e)
Whether an event has everything required to be written out: the long end of every split strip,...
static SlotMap BuildSlotMap()
Build the board/channel to slot lookup for this dataset.
static void ResetEventState(EventState &e)
Clear an event back to its empty state, ready to be seeded.
static Bool_t ShouldKeepHit(ULong64_t cand_ts, ULong64_t prev_ts, UShort_t cand_energy, UShort_t prev_energy, ULong64_t ref_ts, DedupStrategy strategy)
Decide which of two hits on the same slot survives.
static void GetFlagSummary(const EventState &e, Bool_t &has_fake, Bool_t &has_saturation, Bool_t &has_pileup)
Unpack the OR-ed status flags into the conditions that matter.
static void ResetPerChannelData(PerChannelData &p)
Clear the per-slot detail alongside a reset event.
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 AssignHit(EventState &e, PerChannelData *pc, ULong64_t ref_ts, Int_t slot, UShort_t energy, ULong64_t timestamp, UInt_t flags, DedupStrategy strategy)
Place one hit into an event, resolving any collision.
static void SaveSampleTraces(const std::vector< TGraph * > &traces, const TString &save_name, const TString &subdir, Double_t y_min, Double_t y_max, const char *y_title)
Draw a set of traces overlaid on one frame and save it.
static TGraph * BuildTraceFromTotals(const Double_t *total)
Build a trace graph from one event's per-strip totals.
const Int_t ARR_SLOT_CATHODE
Cathode signal.
DedupStrategy ActiveDedupStrategy()
How repeated hits on one slot are resolved.
Double_t ActiveReferenceChannelMaxAdc()
Upper energy gate on the seed channel, in ADC.
Double_t ActiveSeedHoldoffMaxRatio()
The size, as a fraction of the new hit, under which the current seed counts as a pre-trigger.
const TString & ActiveReferenceChannel()
Channel whose hits seed events.
Double_t ActiveLeftEvenMaxAdc()
Left-side ceiling for even strips, in ADC.
Double_t ActiveStripEMaxAdc()
Upper bound of the per-strip energy range, in ADC.
const Int_t ARR_SLOT_GRID
Frisch grid signal.
Double_t ActiveEventTimeWindowUs()
Coincidence window for event building, in microseconds.
Double_t ActiveStripEMinAdc()
Lower bound of the per-strip energy range, in ADC.
const Int_t N_ARR_SLOTS
Length of the per-event channel arrays.
const Int_t ARR_SLOT_STRIP_17
Last anode strip. Strips run contiguously from ARR_SLOT_STRIP_0 to here.
Double_t ActiveRightEvenMaxAdc()
Right-side ceiling for even strips, in ADC.
const std::map< std::pair< Int_t, Int_t >, TString > & ActiveChannelMap()
The channel map in force.
Double_t ActiveLeftOddMaxAdc()
Left-side ceiling for odd strips, in ADC.
Double_t ActiveStrip0MaxAdc()
Strip 0 full scale, in ADC.
Double_t ActiveRightOddMaxAdc()
Right-side ceiling for odd strips, in ADC.
Double_t ActiveCathodeMaxAdc()
Cathode full scale, in ADC.
Double_t ActiveReferenceChannelMinAdc()
Lower energy gate on the seed channel, in ADC.
Int_t ActiveNBoards()
Boards in the active epoch's setup.
const Int_t ARR_SLOT_STRIP_0
First anode strip, occupying the low end of the array.
Double_t ActiveGridMaxAdc()
Grid full scale, in ADC.
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.
Int_t ActiveNChannels()
Channels per board.
Double_t ActiveSeedHoldoffUs()
Seed holdoff in microseconds; 0 = off. See SEED_HOLDOFF_US.
DedupStrategy dedup_strat
Long64_t seeds_close_anodes
Long64_t seeds_close_pair
Long64_t seeds_pretrigger
Long64_t cathode_hits_total
Long64_t dropped_outside_window
Long64_t seeds_close_empty
Int_t empty_channel_map_events
UShort_t leftdE[16]
ADC energies are 14-bit unsigned at the source; store them as UShort_t, not Int_t.
Short_t cathode
14-bit ADC (<=16383), so Short_t holds every value with room to spare while preserving Cathode's -1 "...
Long64_t dropped_cathode_hits_total
Int_t incomplete_with_pileup
Long64_t events_with_multi_cathode
Long64_t miss_long[17]
Per-required-channel miss counts over all events: how often each completeness-required long end or un...
Int_t complete_with_pileup
Long64_t events_with_multi_anode_hit
Int_t complete_with_saturation
Int_t incomplete_with_saturation
Int_t incomplete_with_fake
Long64_t dropped_anode_hits_total
Long64_t events_with_cathode
std::vector< TGraph * > * sample_traces
One built event: the per-slot energies and the flags seen while building it.
Int_t leftdE[16]
Left end of strips 1-16, in ADC, index strip - 1.
UInt_t flags_or
Bitwise OR of the CoMPASS status flags of every hit in the event, so a single test covers pileup or s...
Int_t hits[Constants::N_ARR_SLOTS]
Hit count per slot; see SlotLayout.hpp.
Int_t strip17dE
Strip 17, unsegmented, in ADC.
Bool_t had_cathode
Whether a cathode hit was seen at all.
ULong64_t ref_ts
Timestamp of the reference grid hit that seeded this event.
Int_t cathode
Cathode energy, in ADC.
Int_t strip0dE
Strip 0, unsegmented, in ADC.
Int_t grid
Frisch grid energy, in ADC.
Int_t Total(Int_t strip) const
A strip's deposit: the sum of its ends, or the unsegmented value.
Int_t rightdE[16]
Right end of strips 1-16, in ADC, index strip - 1.
PerChannelData per_channel
std::vector< PendingHit > pending
Raw per-slot detail for one event, kept alongside EventState.
ULong64_t timestamps[Constants::N_ARR_SLOTS]
Hit timestamp per slot.
UInt_t flags[Constants::N_ARR_SLOTS]
Status flags per slot.
UShort_t energies[Constants::N_ARR_SLOTS]
Hit energy per slot, ADC.
Axis ranges and labels for building a SummaryHistograms set.
The standard diagnostic histogram set for a subfile.
TH2F * h_music
Energy against strip index: the MUSIC plot.
TH1F * h1_strip0
Strip 0 energy.
TH1F * h1_cathode
Cathode energy.
TH2F * h2_strip0_vs_grid
Strip 0 against grid energy; strip 1 against the grid when strip 0 is not required of an event (Summa...
TH1F * h_mult
Hit multiplicity per event.
TH1F * h1_strip17
Strip 17 energy.
TH2F * h2_long_vs_short[16]
Long end against short end of the segmented strips 1-16, index strip - 1.
TH1F * h1_grid
Grid energy.