REST-for-Physics  v2.3
Rare Event Searches ToolKit for Physics
TRestRawPeaksFinderProcess.cxx
1
2#include "TRestRawPeaksFinderProcess.h"
3
4#include <utility>
5
7
8using namespace std;
9
11
13 fInputEvent = dynamic_cast<TRestRawSignalEvent*>(inputEvent);
14
15 const auto run = GetRunInfo();
16 if (run != nullptr) {
17 fInputEvent->InitializeReferences(run);
18 }
19
20 if (fReadoutMetadata == nullptr) {
21 fReadoutMetadata = fInputEvent->GetReadoutMetadata();
22 }
23
24 if (fReadoutMetadata == nullptr && !fChannelTypes.empty()) {
25 cerr << "TRestRawPeaksFinderProcess::ProcessEvent: readout metadata is null, cannot filter "
26 "the process by signal type"
27 << endl;
28 exit(1);
29 }
30
31 vector<tuple<UShort_t, UShort_t, double>> eventPeaks; // signalId, time, amplitude
32
33 // Calculate average baseline and sigma of all the TPC signals
34 double BaseLineMean = 0.0;
35 double BaseLineSigmaMean = 0.0;
36 unsigned int countTPC = 0;
37
38 for (int signalIndex = 0; signalIndex < fInputEvent->GetNumberOfSignals(); signalIndex++) {
39 const auto signal = fInputEvent->GetSignal(signalIndex);
40 const UShort_t signalId = signal->GetSignalID();
41
42 const string channelType = fReadoutMetadata->GetTypeForChannelDaqId(signalId);
43 const string channelName = fReadoutMetadata->GetNameForChannelDaqId(signalId);
44
45 // Check if channel type is in the list of selected channel types
46 if (fChannelTypes.find(channelType) == fChannelTypes.end()) {
47 continue;
48 }
49
50 // Choose appropriate function based on channel type
51 if (channelType == "tpc") {
52 signal->CalculateBaseLine(fBaselineRange.X(), fBaselineRange.Y());
53 // Accumulate the baseline and sigma values
54 BaseLineMean += signal->GetBaseLine();
55 BaseLineSigmaMean += signal->GetBaseLineSigma();
56 countTPC++; // Count the signals considered
57 }
58 }
59
60 // Calculate the average if there were any matching signals
61 if (countTPC > 0) {
62 BaseLineMean /= countTPC;
63 BaseLineSigmaMean /= countTPC;
64 }
65
66 for (int signalIndex = 0; signalIndex < fInputEvent->GetNumberOfSignals(); signalIndex++) {
67 const auto signal = fInputEvent->GetSignal(signalIndex);
68 const UShort_t signalId = signal->GetSignalID();
69
70 const string channelType = fReadoutMetadata->GetTypeForChannelDaqId(signalId);
71 const string channelName = fReadoutMetadata->GetNameForChannelDaqId(signalId);
72
73 // check if channel type is in the list of selected channel types
74 if (fChannelTypes.find(channelType) == fChannelTypes.end()) {
75 continue;
76 }
77
78 // Choose appropriate function based on channel type
79 if (channelType == "tpc") {
80 signal->CalculateBaseLine(fBaselineRange.X(), fBaselineRange.Y());
81
82 // I think count will never be 0, just in case
83 const double threshold =
84 (countTPC > 0) ? BaseLineMean + fSigmaOverBaseline * BaseLineSigmaMean
85 : signal->GetBaseLine() + fSigmaOverBaseline * signal->GetBaseLineSigma();
86 if (countTPC <= 0) {
87 cerr << "TRestRawPeaksFinderProcess::ProcessEvent: TPC count is 0 in TPC loop, this should "
88 "not happen"
89 << endl;
90 exit(1);
91 }
92 const auto peaks = signal->GetPeaks(threshold, fDistance);
93
94 for (const auto& [time, amplitude] : peaks) {
95 eventPeaks.emplace_back(signalId, time, amplitude);
96 }
97 } else if (channelType == "veto") {
98 // For veto signals the baseline is calculated over the whole range, as we donĀ“t know where the
99 // signal will be.
100 signal->CalculateBaseLine(0, 511, "OUTLIERS");
101 // For veto signals the threshold is selected by the user.
102 const auto peaks =
103 signal->GetPeaksVeto(signal->GetBaseLine() + fThresholdOverBaseline, fDistance);
104
105 for (const auto& [time, amplitude] : peaks) {
106 eventPeaks.emplace_back(signalId, time, amplitude);
107 }
108 }
109 }
110
111 // sort eventPeaks by time, then signal id
112 sort(eventPeaks.begin(), eventPeaks.end(),
113 [](const tuple<UShort_t, UShort_t, double>& a, const tuple<UShort_t, UShort_t, double>& b) {
114 return tie(get<1>(a), get<0>(a)) < tie(get<1>(b), get<0>(b));
115 });
116
117 vector<UShort_t> peaksChannelId;
118 vector<UShort_t> peaksTime;
119 vector<double> peaksAmplitude;
120
121 double peaksEnergy = 0.0;
122 UShort_t peaksCount = 0;
123 UShort_t peaksCountUnique = 0;
124
125 set<UShort_t> uniquePeaks;
126 for (const auto& [channelId, time, amplitude] : eventPeaks) {
127 peaksChannelId.push_back(channelId);
128 peaksTime.push_back(time);
129 peaksAmplitude.push_back(amplitude);
130
131 peaksEnergy += amplitude;
132 peaksCount++;
133
134 if (uniquePeaks.find(channelId) == uniquePeaks.end()) {
135 uniquePeaks.insert(channelId);
136 peaksCountUnique++;
137 }
138 }
139
140 SetObservableValue("peaksChannelId", peaksChannelId);
141 SetObservableValue("peaksTimeBin", peaksTime);
142 SetObservableValue("peaksAmplitudeADC", peaksAmplitude);
143
144 SetObservableValue("peaksAmplitudeADCSum", peaksEnergy);
145 SetObservableValue("peaksCount", peaksCount);
146 SetObservableValue("peaksCountUnique", peaksCountUnique);
147
148 vector<UShort_t> windowPeakIndex(eventPeaks.size(), std::numeric_limits<UShort_t>::max());
149 vector<UShort_t> windowPeakCenter(eventPeaks.size(), 0);
150 vector<UShort_t> windowPeakMultiplicity(eventPeaks.size(), 0);
151
152 UShort_t window_index = 0;
153 for (size_t peakIndex = 0; peakIndex < eventPeaks.size(); peakIndex++) {
154 const auto& [channelId, time, amplitude] = eventPeaks[peakIndex];
155 const auto windowTimeStart = time - fWindow / 2;
156 const auto windowTimeEnd = time + fWindow / 2;
157
158 // check if the peak is already in a window
159 if (windowPeakIndex[peakIndex] != std::numeric_limits<UShort_t>::max()) {
160 continue;
161 }
162
163 UShort_t window_center_time = time;
164
165 set<UShort_t> windowPeaksIndex;
166
167 // add the peak to the window
168 windowPeaksIndex.insert(peakIndex);
169
170 // add the peaks that are in the window
171 for (size_t otherPeakIndex = peakIndex + 1; otherPeakIndex < eventPeaks.size(); otherPeakIndex++) {
172 const auto& [otherChannelId, otherTime, otherAmplitude] = eventPeaks[otherPeakIndex];
173
174 if (otherTime < windowTimeStart) {
175 continue;
176 }
177
178 if (otherTime > windowTimeEnd) {
179 break;
180 }
181
182 windowPeaksIndex.insert(otherPeakIndex);
183 }
184
185 for (const auto& index : windowPeaksIndex) {
186 windowPeakIndex[index] = window_index;
187 windowPeakCenter[index] = window_center_time;
188 windowPeakMultiplicity[index] = windowPeaksIndex.size();
189 }
190
191 window_index++;
192 }
193
194 SetObservableValue("windowPeakIndex", windowPeakIndex);
195 SetObservableValue("windowPeakTimeBin", windowPeakCenter);
196 SetObservableValue("windowPeakMultiplicity", windowPeakMultiplicity);
197
198 vector<UShort_t> windowCenter;
199 vector<UShort_t> windowMultiplicity;
200
201 set<UShort_t> windowPeaksIndexInserted;
202 for (size_t peakIndex = 0; peakIndex < eventPeaks.size(); peakIndex++) {
203 const auto& window_peak_index = windowPeakIndex[peakIndex];
204 if (windowPeaksIndexInserted.find(window_peak_index) != windowPeaksIndexInserted.end()) {
205 continue;
206 }
207
208 windowPeaksIndexInserted.insert(window_peak_index);
209
210 windowCenter.push_back(windowPeakCenter[peakIndex]);
211 windowMultiplicity.push_back(windowPeakMultiplicity[peakIndex]);
212 }
213
214 SetObservableValue("windowTimeBin", windowCenter);
215 SetObservableValue("windowMultiplicity", windowMultiplicity);
216
217 if (fTimeBinToTimeFactorMultiplier != 0.0) {
218 vector<Double_t> windowCenterTime(windowCenter.size(), 0.0);
219 vector<Double_t> windowPeakCenterTime(windowPeakCenter.size(), 0.0);
220 vector<Double_t> peaksTimePhysical(peaksTime.size(), 0.0);
221
222 // lambda to convert time bin to time
223 auto timeBinToTime = [this](UShort_t timeBin) {
224 if (!fTimeConversionElectronics) {
225 return fTimeBinToTimeFactorMultiplier * timeBin - fTimeBinToTimeFactorOffset;
226 } else {
227 // @jporron
228 double zero = -1 + (512 * fTimeBinToTimeFactorMultiplier - fTimeBinToTimeFactorOffset -
229 fTimeBinToTimeFactorOffsetTCM) /
230 fTimeBinToTimeFactorMultiplier;
231 return fTimeBinToTimeFactorMultiplier * (timeBin - zero);
232 }
233 };
234
235 for (size_t i = 0; i < windowCenter.size(); i++) {
236 windowCenterTime[i] = timeBinToTime(windowCenter[i]);
237 }
238
239 for (size_t i = 0; i < windowPeakCenter.size(); i++) {
240 windowPeakCenterTime[i] = timeBinToTime(windowPeakCenter[i]);
241 }
242
243 for (size_t i = 0; i < peaksTime.size(); i++) {
244 peaksTimePhysical[i] = timeBinToTime(peaksTime[i]);
245 }
246
247 SetObservableValue("windowTime", windowCenterTime);
248 SetObservableValue("windowPeakTime", windowPeakCenterTime);
249 SetObservableValue("peaksTime", peaksTimePhysical);
250 }
251
252 if (fADCtoEnergyFactor != 0.0 || !fChannelIDToADCtoEnergyFactor.empty()) {
253 vector<Double_t> peaksEnergyPhysical(peaksAmplitude.size(), 0.0);
254 Double_t peaksEnergySum = 0.0;
255
256 if (fADCtoEnergyFactor != 0.0) {
257 // same factor for all peaks
258 for (size_t i = 0; i < peaksAmplitude.size(); i++) {
259 peaksEnergyPhysical[i] = peaksAmplitude[i] * fADCtoEnergyFactor;
260 peaksEnergySum += peaksEnergyPhysical[i];
261 }
262 } else {
263 // use map to get factor for each channel
264 for (size_t i = 0; i < peaksAmplitude.size(); i++) {
265 const auto channelId = peaksChannelId[i];
266 // check if the channel is in the map
267 if (fChannelIDToADCtoEnergyFactor.find(channelId) == fChannelIDToADCtoEnergyFactor.end()) {
268 cerr << "TRestRawPeaksFinderProcess::ProcessEvent: channel id " << channelId
269 << " not found in the map of ADC to energy factors" << endl;
270 exit(1);
271 }
272 const auto factor = fChannelIDToADCtoEnergyFactor[channelId];
273
274 peaksEnergyPhysical[i] = peaksAmplitude[i] * factor;
275 peaksEnergySum += peaksEnergyPhysical[i];
276 }
277 }
278
279 SetObservableValue("peaksEnergy", peaksEnergyPhysical);
280 SetObservableValue("peaksEnergySum", peaksEnergySum);
281 }
282
283 // Remove peak-less veto signals after the peak finding if chosen
285 set<UShort_t> peakSignalIds;
286 for (const auto& [channelId, time, amplitude] : eventPeaks) {
287 peakSignalIds.insert(channelId);
288 }
289
290 vector<UShort_t> signalsToRemove;
291 for (int signalIndex = 0; signalIndex < fInputEvent->GetNumberOfSignals(); signalIndex++) {
292 const auto signal = fInputEvent->GetSignal(signalIndex);
293 const UShort_t signalId = signal->GetSignalID();
294 const string signalType = fReadoutMetadata->GetTypeForChannelDaqId(signalId);
295
296 if (signalType == "veto" && peakSignalIds.find(signalId) == peakSignalIds.end()) {
297 signalsToRemove.push_back(signalId);
298 }
299 }
300
301 // Now remove all veto signals identified
302 for (const auto& signalId : signalsToRemove) {
303 fInputEvent->RemoveSignalWithId(signalId);
304 }
305 }
306
307 // Remove all veto signals after the peak finding if chosen
308 if (fRemoveAllVetoes) {
309 vector<UShort_t> signalsToRemove;
310 for (int signalIndex = 0; signalIndex < fInputEvent->GetNumberOfSignals(); signalIndex++) {
311 const auto signal = fInputEvent->GetSignal(signalIndex);
312 const UShort_t signalId = signal->GetSignalID();
313 const string signalType = fReadoutMetadata->GetTypeForChannelDaqId(signalId);
314
315 if (signalType == "veto") {
316 signalsToRemove.push_back(signalId);
317 }
318 }
319
320 // Now remove all veto signals identified
321 for (const auto& signalId : signalsToRemove) {
322 fInputEvent->RemoveSignalWithId(signalId);
323 }
324 }
325
326 return inputEvent;
327}
328
329std::map<UShort_t, double> parseStringToMap(const std::string& input) {
330 std::map<UShort_t, double> my_map;
331 std::string cleaned_input;
332
333 // Step 1: Clean the input to remove curly braces and spaces
334 for (char c : input) {
335 if (!std::isspace(c) && c != '{' && c != '}') {
336 cleaned_input += c;
337 }
338 }
339
340 // Step 2: Parse each key-value pair
341 std::stringstream ss(cleaned_input);
342 std::string pair;
343 while (std::getline(ss, pair, ',')) {
344 // Find the colon to split key and value
345 size_t colon_pos = pair.find(':');
346 if (colon_pos != std::string::npos) {
347 // Extract and convert key and value
348 int key = std::stoi(pair.substr(0, colon_pos));
349 double value = std::stod(pair.substr(colon_pos + 1));
350 my_map[key] = value;
351 }
352 }
353
354 return my_map;
355}
356
358 const auto filterType = GetParameter("channelType", "");
359 if (!filterType.empty()) {
360 fChannelTypes.insert(filterType);
361 }
362
363 if (fChannelTypes.empty()) {
364 // if no channel type is specified, use all channel types
365 }
366
367 fThresholdOverBaseline = GetDblParameterWithUnits("thresholdOverBaseline", fThresholdOverBaseline);
368 fSigmaOverBaseline = GetDblParameterWithUnits("sigmaOverBaseline", fSigmaOverBaseline);
369 fBaselineRange = Get2DVectorParameterWithUnits("baselineRange", fBaselineRange);
370 fDistance = UShort_t(GetDblParameterWithUnits("distance", fDistance));
371 fWindow = UShort_t(GetDblParameterWithUnits("window", fWindow));
372 fRemoveAllVetoes = StringToBool(GetParameter("removeAllVetoes", fRemoveAllVetoes));
373 fRemovePeaklessVetoes = StringToBool(GetParameter("removePeaklessVetoes", fRemovePeaklessVetoes));
374
375 fTimeBinToTimeFactorMultiplier = GetDblParameterWithUnits("sampling", fTimeBinToTimeFactorMultiplier);
376 fTimeBinToTimeFactorOffset = GetDblParameterWithUnits("trigDelay", fTimeBinToTimeFactorOffset);
377 fTimeBinToTimeFactorOffsetTCM = GetDblParameterWithUnits("trigDelayTCM", fTimeBinToTimeFactorOffsetTCM);
378 fTimeConversionElectronics =
379 StringToBool(GetParameter("trigDelayElectronics", fTimeConversionElectronics));
380
381 fADCtoEnergyFactor = GetDblParameterWithUnits("adcToEnergyFactor", fADCtoEnergyFactor);
382 const string fChannelIDToADCtoEnergyFactorAsString = GetParameter("channelIDToADCtoEnergyFactor", "");
383 if (!fChannelIDToADCtoEnergyFactorAsString.empty()) {
384 // map should be in the format: "{channelId1: factor1, channelId2: factor2, ...}" (spaces are allowed
385 // but not required)
386 fChannelIDToADCtoEnergyFactor = parseStringToMap(fChannelIDToADCtoEnergyFactorAsString);
387 }
388
389 if (fADCtoEnergyFactor != 0 && !fChannelIDToADCtoEnergyFactor.empty()) {
390 cerr << "TRestRawPeaksFinderProcess::InitFromConfigFile: both adcToEnergyFactor and "
391 "channelIDToADCtoEnergyFactor are defined. Please, remove one of them."
392 << endl;
393 exit(1);
394 }
395
396 if (fBaselineRange.X() > fBaselineRange.Y() || fBaselineRange.X() < 0 || fBaselineRange.Y() < 0) {
397 cerr << "TRestRawPeaksFinderProcess::InitProcess: baseline range is not sorted or < 0" << endl;
398 exit(1);
399 }
400
401 if (fThresholdOverBaseline < 0) {
402 cerr << "TRestRawPeaksFinderProcess::InitProcess: threshold over baseline is < 0" << endl;
403 exit(1);
404 }
405
406 if (fSigmaOverBaseline < 0) {
407 cerr << "TRestRawPeaksFinderProcess::InitProcess: sigma over baseline is < 0" << endl;
408 exit(1);
409 }
410
411 if (fDistance <= 0) {
412 cerr << "TRestRawPeaksFinderProcess::InitProcess: distance is < 0" << endl;
413 exit(1);
414 }
415
416 if (fWindow <= 0) {
417 cerr << "TRestRawPeaksFinderProcess::InitProcess: window is < 0" << endl;
418 exit(1);
419 }
420
421 if (filterType != "veto" && fRemovePeaklessVetoes) {
422 cerr << "TRestRawPeaksFinderProcess::InitProcess: removing veto signals only makes sense when the "
423 "process is applied to veto signals. Remove \"removePeaklessVetoes\" parameter"
424 << endl;
425 exit(1);
426 }
427}
428
431
432 RESTMetadata << "Applying process to channel types: ";
433 for (const auto& type : fChannelTypes) {
434 RESTMetadata << type << " ";
435 }
436 RESTMetadata << RESTendl;
437
438 RESTMetadata << "Baseline range for tpc signals: " << fBaselineRange.X() << " - " << fBaselineRange.Y()
439 << RESTendl;
440 RESTMetadata << "Sigma over baseline for tpc signals: " << fSigmaOverBaseline << RESTendl;
441
442 RESTMetadata << "Threshold over baseline for veto signals: " << fThresholdOverBaseline << RESTendl;
443
444 RESTMetadata << "Distance: " << fDistance << RESTendl;
445 RESTMetadata << "Window: " << fWindow << RESTendl;
446
447 EndPrintProcess();
448}
TRestRun * GetRunInfo() const
Return the pointer of the hosting TRestRun object.
void BeginPrintProcess()
[name, cut range]
void SetObservableValue(const std::string &name, const T &value)
Set observable value for AnalysisTree.
A base class for any REST event.
Definition: TRestEvent.h:38
virtual void InitializeReferences(TRestRun *run)
Initialize dynamical references when loading the event from a root file.
Definition: TRestEvent.cxx:175
endl_t RESTendl
Termination flag object for TRestStringOutput.
std::string GetParameter(std::string parName, TiXmlElement *e, TString defaultValue=PARAMETER_NOT_FOUND_STR)
Returns the value for the parameter named parName in the given section.
Double_t fSigmaOverBaseline
choose times the sigma of the baseline must be overcome to consider a peak
UShort_t fDistance
distance between two peaks to consider them as different (ADC units)
void InitFromConfigFile() override
To make settings from rml file. This method must be implemented in the derived class.
Bool_t fRemovePeaklessVetoes
option to remove peak-less veto signals after finding the peaks
Double_t fThresholdOverBaseline
threshold over baseline to consider a peak
UShort_t fWindow
window size to calculate the peak amplitude (time bins)
void InitProcess() override
To be executed at the beginning of the run (outside event loop)
TRestEvent * ProcessEvent(TRestEvent *inputEvent) override
Process one event.
TVector2 fBaselineRange
range of samples to calculate baseline for peak finding
void PrintMetadata() override
Implemented it in the derived metadata class to print out specific metadata information.
Bool_t fRemoveAllVetoes
option to remove all veto signals after finding the peaks
An event container for time rawdata signals with fixed length.
Int_t GetSignalID() const
Returns the value of signal ID.