REST-for-Physics  v2.3
Rare Event Searches ToolKit for Physics
TRestDetectorSignalToRawSignalProcess.cxx
1 /*************************************************************************
2  * This file is part of the REST software framework. *
3  * *
4  * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza) *
5  * For more information see http://gifna.unizar.es/trex *
6  * *
7  * REST is free software: you can redistribute it and/or modify *
8  * it under the terms of the GNU General Public License as published by *
9  * the Free Software Foundation, either version 3 of the License, or *
10  * (at your option) any later version. *
11  * *
12  * REST is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have a copy of the GNU General Public License along with *
18  * REST in $REST_PATH/LICENSE. *
19  * If not, see http://www.gnu.org/licenses/. *
20  * For the list of contributors see $REST_PATH/CREDITS. *
21  *************************************************************************/
22 
135 
136 #include "TRestDetectorSignalToRawSignalProcess.h"
137 
138 #include <TObjString.h>
139 #include <TRestRawReadoutMetadata.h>
140 
141 #include <limits>
142 
143 using namespace std;
144 
146 
151 
165  Initialize();
166  LoadConfig(configFilename);
167 }
168 
173  delete fOutputRawSignalEvent;
174 }
175 
188 void TRestDetectorSignalToRawSignalProcess::LoadConfig(const string& configFilename, const string& name) {
189  LoadConfigFromFile(configFilename, name);
190 }
191 
197  SetSectionName(this->ClassName());
198  SetLibraryVersion(LIBRARY_VERSION);
199 
200  fInputSignalEvent = nullptr;
201  fOutputRawSignalEvent = new TRestRawSignalEvent();
202 }
203 
208  fInputSignalEvent = (TRestDetectorSignalEvent*)inputEvent;
209 
210  if (fInputSignalEvent->GetNumberOfSignals() <= 0) {
211  return nullptr;
212  }
213 
214  if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Debug) {
215  fOutputRawSignalEvent->PrintEvent();
216  }
217 
218  fOutputRawSignalEvent->SetID(fInputSignalEvent->GetID());
219  fOutputRawSignalEvent->SetSubID(fInputSignalEvent->GetSubID());
220  fOutputRawSignalEvent->SetTimeStamp(fInputSignalEvent->GetTimeStamp());
221  fOutputRawSignalEvent->SetSubEventTag(fInputSignalEvent->GetSubEventTag());
222 
223  double startTimeNoOffset = 0;
224 
225  if (fTriggerMode == "firstDeposit") {
226  startTimeNoOffset = fInputSignalEvent->GetMinTime();
227  } else if (fTriggerMode == "integralThreshold") {
228  bool thresholdReached = false;
229  for (Double_t t = fInputSignalEvent->GetMinTime() - fNPoints * fSampling;
230  t <= fInputSignalEvent->GetMaxTime() + fNPoints * fSampling; t = t + 0.5) {
231  Double_t energy = fInputSignalEvent->GetIntegralWithTime(t, t + (fSampling * fNPoints) / 2.);
232 
233  if (energy > fIntegralThreshold) {
234  startTimeNoOffset = t;
235  thresholdReached = true;
236  }
237  }
238  if (!thresholdReached) {
239  RESTWarning << "Integral threshold for trigger not reached" << RESTendl;
240  startTimeNoOffset = 0;
241  }
242  } else if (fTriggerMode == "observable") {
243  const auto obs = GetObservableValue<double>(fTriggerModeObservableName);
244  startTimeNoOffset = obs;
245 
246  } else if (fTriggerMode == "firstDepositTPC" || fTriggerMode == "integralThresholdTPC") {
247  fReadout = GetMetadata<TRestDetectorReadout>();
248 
249  if (fReadout == nullptr) {
250  RESTError << "TRestDetectorSignalToRawSignalProcess::ProcessEvent: "
251  << "TRestDetectorReadout metadata not found" << RESTendl;
252  exit(1);
253  }
254 
255  set<const TRestDetectorSignal*> tpcSignals;
256 
257  for (int n = 0; n < fInputSignalEvent->GetNumberOfSignals(); n++) {
258  TRestDetectorSignal* signal = fInputSignalEvent->GetSignal(n);
259  if (signal->GetSignalType() == "tpc") {
260  tpcSignals.insert(signal);
261  }
262  /*
263  const auto allDaqIds = fReadout->GetAllDaqIds();
264  for (const auto& daqId : allDaqIds) {
265  const auto& channel = fReadout->GetReadoutChannelWithDaqID(daqId);
266  // TODO: sometimes channel type does not match signal type, why?
267  }
268  */
269  }
270 
271  if (tpcSignals.empty()) {
272  return nullptr;
273  }
274 
275  if (fTriggerMode == "firstDepositTPC") {
276  double startTime = std::numeric_limits<float>::max();
277  for (const auto& signal : tpcSignals) {
278  const auto minTime = signal->GetMinTime();
279  if (minTime < startTime) {
280  startTime = minTime;
281  }
282  }
283 
284  if (startTime >= std::numeric_limits<float>::max()) {
285  return nullptr;
286  }
287  startTimeNoOffset = startTime;
288 
289  } else if (fTriggerMode == "integralThresholdTPC") {
290  RESTDebug << "TRestDetectorSignalToRawSignalProcess::ProcessEvent: "
291  << "Trigger mode integralThresholdTPC" << RESTendl;
292 
293  const double signalIntegralThreshold = 0.5; // keV
294  double totalEnergy = 0;
295  for (const auto& signal : tpcSignals) {
296  totalEnergy += signal->GetIntegral();
297  }
298  if (totalEnergy < signalIntegralThreshold) {
299  return nullptr;
300  }
301 
302  double maxTime = 0;
303  double minTime = std::numeric_limits<float>::max();
304  for (const auto& signal : tpcSignals) {
305  const auto maxSignalTime = signal->GetMaxTime();
306  if (maxSignalTime > maxTime) {
307  maxTime = maxSignalTime;
308  }
309  const auto minSignalTime = signal->GetMinTime();
310  if (minSignalTime < minTime) {
311  minTime = minSignalTime;
312  }
313  }
314 
315  // lots of problem with signal methods (GetMinTime, GetMaxTime, etc.)
316  if (minTime > maxTime) {
317  // TODO: this should raise an exception
318  RESTWarning << "TRestDetectorSignalToRawSignalProcess::ProcessEvent: "
319  << "minTime > maxTime" << RESTendl;
320  // exit(1);
321  return nullptr;
322  }
323  if (minTime < 0) {
324  RESTWarning
325  << "TRestDetectorSignalToRawSignalProcess::ProcessEvent: " << inputEvent->GetID()
326  << " signal minTime < 0. Setting min time to 0, but this should probably never happen"
327  << RESTendl;
328  minTime = 0;
329  // TODO: this should raise an exception
330  // exit(1);
331  }
332 
333  double t = minTime;
334  bool thresholdReached = false;
335  double maxEnergy = 0;
336  while (t < maxTime) {
337  // iterate over number of signals
338  double energy = 0;
339  for (const auto& signal : tpcSignals) {
340  energy += signal->GetIntegralWithTime(t - fSampling * fNPoints, t);
341  }
342  if (energy > maxEnergy) {
343  maxEnergy = energy;
344  }
345  if (maxEnergy > signalIntegralThreshold) {
346  thresholdReached = true;
347  break;
348  }
349  t += fSampling;
350  }
351 
352  if (!thresholdReached) {
353  /*
354  cout << "TRestDetectorSignalToRawSignalProcess::ProcessEvent: "
355  << "Integral threshold for trigger not reached. Maximum energy reached: " << maxEnergy
356  << endl;
357  */
358  return nullptr;
359  }
360 
361  double startTime = t;
362  startTimeNoOffset = startTime;
363 
364  SetObservableValue("triggerTimeTPC", startTime);
365  }
366 
367  } else if (fTriggerMode == "fixed") {
368  startTimeNoOffset = fTriggerFixedStartTime;
369  } else {
370  cerr << "TRestDetectorSignalToRawSignalProcess::ProcessEvent: "
371  << "Trigger mode not recognized" << RESTendl;
372  exit(1);
373  }
374 
375  for (int n = 0; n < fInputSignalEvent->GetNumberOfSignals(); n++) {
376  TRestDetectorSignal* signal = fInputSignalEvent->GetSignal(n);
377  Int_t signalID = signal->GetSignalID();
378  string type = signal->GetSignalType();
379  // Check type is in the map
380  if (fParametersMap.find(type) == fParametersMap.end()) {
381  RESTWarning << "TRestDetectorSignalToRawSignalProcess::ProcessEvent: "
382  << "type " << type << " not found in parameters map" << RESTendl;
383  type = "";
384  }
385 
386  double sampling = fParametersMap.at(type).sampling;
387  double shapingTime = fParametersMap.at(type).shapingTime;
388  double calibrationGain = fParametersMap.at(type).calibrationGain;
389  double calibrationOffset = fParametersMap.at(type).calibrationOffset;
390 
391  double timeStart = startTimeNoOffset - fTriggerDelay * sampling;
392  double timeEnd = timeStart + fNPoints * sampling;
393  RESTDebug << "fTimeStart: " << timeStart << " us " << RESTendl;
394  RESTDebug << "fTimeEnd: " << timeEnd << " us " << RESTendl;
395 
396  if (timeStart + fTriggerDelay * sampling < 0) {
397  // This means something is wrong (negative times somewhere). This should never happen
398  cerr << "TRestDetectorSignalToRawSignalProcess::ProcessEvent: "
399  << "fTimeStart < - fTriggerDelay * fSampling" << endl;
400  exit(1);
401  }
402 
403  // TODO: time offset may not be working correctly
404  // TODO: event drawing not working correctly (some signals are clipped)
405 
406  if (timeStart + fTriggerDelay * fSampling < 0) {
407  // This means something is wrong (negative times somewhere). This should never happen
408  RESTError << "TRestDetectorSignalToRawSignalProcess::ProcessEvent: "
409  << "fTimeStart < - fTriggerDelay * fSampling" << RESTendl;
410  exit(1);
411  }
412 
413  vector<Double_t> data(fNPoints, calibrationOffset);
414 
415  for (int m = 0; m < signal->GetNumberOfPoints(); m++) {
416  Double_t t = signal->GetTime(m);
417  Double_t d = signal->GetData(m);
418 
419  if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Debug && n < 3 && m < 5) {
420  cout << "Signal: " << n << " Sample: " << m << " T: " << t << " Data: " << d << endl;
421  }
422 
423  if (t > timeStart && t < timeEnd) {
424  // convert physical time (in us) to timeBin
425  Int_t timeBin = (Int_t)round((t - timeStart) / sampling);
426 
427  if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Warning) {
428  if (timeBin < 0 || timeBin > fNPoints) {
429  cout << "Time bin out of range!!! bin value: " << timeBin << endl;
430  timeBin = 0;
431  }
432  }
433 
434  RESTDebug << "Adding data: " << signal->GetData(m) << " to Time Bin: " << timeBin << RESTendl;
435  data[timeBin] += calibrationGain * signal->GetData(m);
436  }
437  }
438 
439  if (shapingTime > 0) {
440  const auto sinShaper = [](Double_t t) -> Double_t {
441  if (t <= 0) {
442  return 0;
443  }
444  // function is normalized such that its absolute maximum is 1.0
445  // max is at x = 1.1664004483744728
446  return TMath::Exp(-3.0 * t) * TMath::Power(t, 3.0) * TMath::Sin(t) * 22.68112123672292;
447  };
448 
449  const auto shapingFunction = [&sinShaper](Double_t t) -> Double_t {
450  if (t <= 0) {
451  return 0;
452  }
453  // function is normalized such that its absolute maximum is 1.0
454  // max is at x = 1.1664004483744728
455  // return sinShaper(t) - 1.0 * sinShaper(t - 1); // to add undershoot
456  return sinShaper(t);
457  };
458 
459  vector<Double_t> dataAfterShaping(fNPoints, calibrationOffset);
460  for (int i = 0; i < fNPoints; i++) {
461  const Double_t value = data[i] - calibrationOffset;
462  if (value <= 0) {
463  // Only positive values are possible, 0 means no signal in this bin
464  continue;
465  }
466  for (int j = 0; j < fNPoints; j++) {
467  dataAfterShaping[j] += value * shapingFunction(((j - i) * sampling) / shapingTime);
468  }
469  }
470  data = dataAfterShaping;
471  }
472 
473  TRestRawSignal rawSignal;
474  rawSignal.SetSignalID(signalID);
475  for (int x = 0; x < fNPoints; x++) {
476  double value = round(data[x]);
477  if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Warning) {
478  if (value < numeric_limits<Short_t>::min() || value > numeric_limits<Short_t>::max()) {
479  RESTDebug << "value (" << value << ") is outside short range ("
480  << numeric_limits<Short_t>::min() << ", " << numeric_limits<Short_t>::max()
481  << ")" << RESTendl;
482  }
483  }
484 
485  if (value < numeric_limits<Short_t>::min()) {
486  value = numeric_limits<Short_t>::min();
487  fOutputRawSignalEvent->SetOK(false);
488  } else if (value > numeric_limits<Short_t>::max()) {
489  value = numeric_limits<Short_t>::max();
490  fOutputRawSignalEvent->SetOK(false);
491  }
492  rawSignal.AddPoint((Short_t)value);
493  }
494 
495  if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Debug) {
496  rawSignal.Print();
497  }
498  RESTDebug << "Adding signal to raw signal event" << RESTendl;
499 
500  fOutputRawSignalEvent->AddSignal(rawSignal);
501  }
502 
503  RESTDebug << "TRestDetectorSignalToRawSignalProcess. Returning event with N signals "
504  << fOutputRawSignalEvent->GetNumberOfSignals() << RESTendl;
505 
506  return fOutputRawSignalEvent;
507 }
508 
514  TString readoutTypesString = GetParameter("readoutTypes", "");
515  // split it by ","
516  TObjArray* readoutTypesArray = readoutTypesString.Tokenize(",");
517  for (int i = 0; i < readoutTypesArray->GetEntries(); i++) {
518  fReadoutTypes.insert(((TObjString*)readoutTypesArray->At(i))->GetString().Data());
519  }
520  cout << "readout types: ";
521  for (const auto& type : fReadoutTypes) {
522  cout << type << " ";
523  }
524  cout << endl;
525 
526  // add default type ""
527  const string defaultType;
528  fReadoutTypes.insert(defaultType);
529  fParametersMap[defaultType] = {};
530 
531  for (const auto& type : fReadoutTypes) {
532  fReadoutTypes.insert(type);
533  fParametersMap[type] = {};
534 
535  string typeCamelCase = type;
536  typeCamelCase[0] = toupper(typeCamelCase[0]);
537 
538  Parameters parameters;
539  parameters.sampling = GetDblParameterWithUnits("sampling" + typeCamelCase, parameters.sampling);
540  parameters.shapingTime =
541  GetDblParameterWithUnits("shapingTime" + typeCamelCase, parameters.shapingTime);
542  parameters.calibrationGain =
543  GetDblParameterWithUnits("gain" + typeCamelCase, parameters.calibrationGain);
544  parameters.calibrationOffset =
545  GetDblParameterWithUnits("offset" + typeCamelCase, parameters.calibrationOffset);
546  parameters.calibrationEnergy =
547  Get2DVectorParameterWithUnits("calibrationEnergy" + typeCamelCase, parameters.calibrationEnergy);
548  parameters.calibrationRange =
549  Get2DVectorParameterWithUnits("calibrationRange" + typeCamelCase, parameters.calibrationRange);
550 
551  const bool isLinearCalibration =
552  (parameters.calibrationEnergy.Mod() != 0 && parameters.calibrationRange.Mod() != 0);
553  ;
554  if (isLinearCalibration) {
555  const auto range = numeric_limits<Short_t>::max() - numeric_limits<Short_t>::min();
556  parameters.calibrationGain =
557  range * (parameters.calibrationRange.Y() - parameters.calibrationRange.X()) /
558  (parameters.calibrationEnergy.Y() - parameters.calibrationEnergy.X());
559  parameters.calibrationOffset =
560  range * (parameters.calibrationRange.X() -
561  parameters.calibrationGain * parameters.calibrationEnergy.X()) +
562  numeric_limits<Short_t>::min();
563  }
564  fParametersMap[type] = parameters;
565  }
566 
567  auto nPoints = GetParameter("nPoints");
568  if (nPoints == PARAMETER_NOT_FOUND_STR) {
569  nPoints = GetParameter("Npoints", fNPoints);
570  }
571  fNPoints = StringToInteger(nPoints);
572 
573  fTriggerMode = GetParameter("triggerMode", fTriggerMode);
574  const set<string> validTriggerModes = {"firstDeposit", "integralThreshold", "fixed",
575  "observable", "firstDepositTPC", "integralThresholdTPC"};
576  if (validTriggerModes.count(fTriggerMode) == 0) {
577  RESTError << "Trigger mode set to: '" << fTriggerMode
578  << "' which is not a valid trigger mode. Please use one of the following trigger modes: ";
579  for (const auto& triggerMode : validTriggerModes) {
580  RESTError << triggerMode << " ";
581  }
582  RESTError << RESTendl;
583  exit(1);
584  }
585 
586  fTriggerDelay = StringToInteger(GetParameter("triggerDelay", fTriggerDelay));
587  fIntegralThreshold = StringToDouble(GetParameter("integralThreshold", fIntegralThreshold));
588  fTriggerFixedStartTime = GetDblParameterWithUnits("triggerFixedStartTime", fTriggerFixedStartTime);
589 
590  // load default parameters (for backward compatibility)
591  fSampling = fParametersMap.at(defaultType).sampling;
592  fShapingTime = fParametersMap.at(defaultType).shapingTime;
593  fCalibrationGain = fParametersMap.at(defaultType).calibrationGain;
594  fCalibrationOffset = fParametersMap.at(defaultType).calibrationOffset;
595  fCalibrationEnergy = fParametersMap.at(defaultType).calibrationEnergy;
596  fCalibrationRange = fParametersMap.at(defaultType).calibrationRange;
597 
598  if (fTriggerMode == "observable") {
599  fTriggerModeObservableName = GetParameter("triggerModeObservableName", "");
600  if (fTriggerModeObservableName.empty()) {
601  RESTError << "You need to set 'triggerModeObservableName' to a valid analysis tree observable"
602  << RESTendl;
603  exit(1);
604  }
605  }
606 }
607 
609 
610 Double_t TRestDetectorSignalToRawSignalProcess::GetEnergyFromADC(Double_t adc, const string& type) const {
611  if (fParametersMap.find(type) == fParametersMap.end()) {
612  RESTWarning << "TRestDetectorSignalToRawSignalProcess::GetEnergyFromADC: "
613  << "type " << type << " not found in parameters map" << RESTendl;
614  return 0;
615  }
616  const auto gain = fParametersMap.at(type).calibrationGain;
617  const auto offset = fParametersMap.at(type).calibrationOffset;
618  return (adc - offset) / gain;
619 }
620 
621 Double_t TRestDetectorSignalToRawSignalProcess::GetADCFromEnergy(Double_t energy, const string& type) const {
622  if (fParametersMap.find(type) == fParametersMap.end()) {
623  RESTWarning << "TRestDetectorSignalToRawSignalProcess::GetEnergyFromADC: "
624  << "type " << type << " not found in parameters map" << RESTendl;
625  return 0;
626  }
627  const auto gain = fParametersMap.at(type).calibrationGain;
628  const auto offset = fParametersMap.at(type).calibrationOffset;
629  return energy * gain + offset;
630 }
631 
632 Double_t TRestDetectorSignalToRawSignalProcess::GetTimeFromBin(Double_t bin, const string& type) const {
633  if (fParametersMap.find(type) == fParametersMap.end()) {
634  RESTWarning << "TRestDetectorSignalToRawSignalProcess::GetEnergyFromADC: "
635  << "type " << type << " not found in parameters map" << RESTendl;
636  return 0;
637  }
638  const auto sampling = fParametersMap.at(type).sampling;
639  return (bin - fTriggerDelay) * sampling;
640 }
641 
642 Double_t TRestDetectorSignalToRawSignalProcess::GetBinFromTime(Double_t time, const string& type) const {
643  if (fParametersMap.find(type) == fParametersMap.end()) {
644  RESTWarning << "TRestDetectorSignalToRawSignalProcess::GetEnergyFromADC: "
645  << "type " << type << " not found in parameters map" << RESTendl;
646  return 0;
647  }
648  const auto sampling = fParametersMap.at(type).sampling;
649  return (UShort_t)((time + fTriggerDelay * sampling) / sampling);
650 }
651 
653  BeginPrintProcess();
654 
655  RESTMetadata << "Points per channel: " << fNPoints << RESTendl;
656  RESTMetadata << "Trigger mode: " << fTriggerMode << RESTendl;
657  RESTMetadata << "Trigger delay: " << fTriggerDelay << " units" << RESTendl;
658 
659  for (const auto& readoutType : fReadoutTypes) {
660  RESTMetadata << RESTendl;
661  string type = readoutType;
662  if (type.empty()) {
663  type = "default";
664  }
665  RESTMetadata << "Readout type: " << type << RESTendl;
666  RESTMetadata << "Sampling time: " << fParametersMap.at(readoutType).sampling * 1000 << " ns"
667  << RESTendl;
668  const double shapingTime = fParametersMap.at(readoutType).shapingTime;
669  if (shapingTime > 0) {
670  RESTMetadata << "Shaping time: " << shapingTime * 1000 << " ns" << RESTendl;
671  }
672 
673  if (IsLinearCalibration()) {
674  RESTMetadata << "Calibration energies: (" << fParametersMap.at(readoutType).calibrationEnergy.X()
675  << ", " << fParametersMap.at(readoutType).calibrationEnergy.Y() << ") keV"
676  << RESTendl;
677  RESTMetadata << "Calibration range: (" << fParametersMap.at(readoutType).calibrationRange.X()
678  << ", " << fParametersMap.at(readoutType).calibrationRange.Y() << ")" << RESTendl;
679  }
680  RESTMetadata << "ADC Gain: " << fParametersMap.at(readoutType).calibrationGain << RESTendl;
681  RESTMetadata << "ADC Offset: " << fParametersMap.at(readoutType).calibrationOffset << RESTendl;
682  }
683  EndPrintProcess();
684 }
A process to convert a TRestDetectorSignalEvent into a TRestRawSignalEvent.
TRestEvent * ProcessEvent(TRestEvent *inputEvent) override
The main processing event function.
void Initialize() override
Function to initialize input/output event members and define the section name.
void InitProcess() override
To be executed at the beginning of the run (outside event loop)
void PrintMetadata() override
It prints out the process parameters stored in the metadata structure.
void LoadConfig(const std::string &configFilename, const std::string &name="")
Function to load the configuration from an external configuration file.
void InitFromConfigFile() override
Function reading input parameters from the RML TRestDetectorSignalToRawSignalProcess metadata section...
A base class for any REST event.
Definition: TRestEvent.h:38
virtual void PrintEvent() const
Definition: TRestEvent.cxx:187
An event container for time rawdata signals with fixed length.
It defines a Short_t array with a physical parameter that evolves in time using a fixed time bin.
void Print() const
It prints the signal data on screen.
void SetSignalID(Int_t sID)
It sets the id number of the signal.
void AddPoint(Short_t)
Adds a new point to the end of the signal data array.
@ REST_Debug
+show the defined debug messages
Double_t StringToDouble(std::string in)
Gets a double from a string.
Int_t StringToInteger(std::string in)
Gets an integer from a string.