REST-for-Physics  v2.3
Rare Event Searches ToolKit for Physics
TRestDetectorSingleChannelAnalysisProcess.cxx
1 
17 #include "TRestDetectorSingleChannelAnalysisProcess.h"
18 
19 #include <TFitResult.h>
20 #include <TLatex.h>
21 #include <TLegend.h>
22 #include <TLine.h>
23 #include <TPaveText.h>
24 #include <TRandom.h>
25 #include <TSpectrum.h>
26 
27 using namespace std;
28 
30 
31 TRestDetectorSingleChannelAnalysisProcess::TRestDetectorSingleChannelAnalysisProcess() { Initialize(); }
32 
33 TRestDetectorSingleChannelAnalysisProcess::~TRestDetectorSingleChannelAnalysisProcess() {}
34 
36  SetSectionName(this->ClassName());
37  SetLibraryVersion(LIBRARY_VERSION);
38 
39  fSignalEvent = nullptr;
40 
41  fReadout = nullptr;
42 }
43 
45  fReadout = GetMetadata<TRestDetectorReadout>();
46  fCalib = GetMetadata<TRestDetectorGainMap>();
47  if (fReadout == nullptr) {
48  } else {
49  for (int i = 0; i < fReadout->GetNumberOfReadoutPlanes(); i++) {
50  auto plane = fReadout->GetReadoutPlane(i);
51  for (size_t j = 0; j < plane->GetNumberOfModules(); j++) {
52  auto mod = plane->GetModule(j);
53  for (size_t k = 0; k < mod->GetNumberOfChannels(); k++) {
54  auto channel = mod->GetChannel(k);
55  fChannelGain[channel->GetDaqID()] = 1; // default correction factor is 1
56  fChannelGainError[channel->GetDaqID()] = 1; // relative error
57  fChannelThrIntegral[channel->GetDaqID()] =
58  new TH1D(Form("h%i", channel->GetDaqID()), Form("h%i", channel->GetDaqID()), 100, 0,
59  fSpecFitRange.Y() * 1.5);
60  }
61  }
62  }
63  }
64 
65  if (fApplyGainCorrection) {
66  if (fCalib != nullptr) {
67  for (auto iter = fChannelGain.begin(); iter != fChannelGain.end(); iter++) {
68  if (fCalib->fChannelGain.count(iter->first) == 0) {
69  RESTError << "in consistent gain mapping and readout definition!" << RESTendl;
70  RESTError << "channel: " << iter->first << " not fount in mapping file!" << RESTendl;
71  abort();
72  }
73  }
74 
75  } else {
76  RESTError << "You must set a TRestDetectorGainMap metadata object to apply gain correction!"
77  << RESTendl;
78  abort();
79  }
80  }
81 
82  if (GetFriend("TRestRawSignalAnalysisProcess") == nullptr) {
83  RESTError << "please add friend process TRestRawSignalAnalysisProcess and "
84  "TRestRawReadoutAnalysisProcess "
85  "and turn on all their observables!"
86  << RESTendl;
87  abort();
88  }
89 }
90 
92  fSignalEvent = (TRestDetectorSignalEvent*)inputEvent;
93 
94  Double_t new_PeakAmplitudeIntegral = 0;
95  Double_t new_ThresholdIntegral = 0;
96 
97  map<int, Double_t> sAna_max_amplitude_map = GetObservableValue<map<int, Double_t>>("max_amplitude_map");
98  map<int, Double_t> sAna_thr_integral_map = GetObservableValue<map<int, Double_t>>("thr_integral_map");
99  Double_t sAna_ThresholdIntegral = GetObservableValue<Double_t>("ThresholdIntegral");
100  Double_t sAna_NumberOfGoodSignals = GetObservableValue<int>("NumberOfGoodSignals");
101 
102  if (fCreateGainMap) {
103  if ((sAna_ThresholdIntegral > fThrIntegralCutRange.X() &&
104  sAna_ThresholdIntegral < fThrIntegralCutRange.Y()) &&
105  (sAna_NumberOfGoodSignals > fNGoodSignalsCutRange.X() &&
106  sAna_NumberOfGoodSignals < fNGoodSignalsCutRange.Y())) {
107  // if within energy cut range
108 
109  for (auto iter = sAna_thr_integral_map.begin(); iter != sAna_thr_integral_map.end(); iter++) {
110  if (fChannelThrIntegral[iter->first] == nullptr) {
111  fChannelThrIntegral[iter->first] = new TH1D(
112  Form("h%i", iter->first), Form("h%i", iter->first), 100, 0, fSpecFitRange.Y() * 1.5);
113  }
114 
115  fChannelThrIntegral[iter->first]->Fill(iter->second);
116  }
117  }
118  }
119 
120  else if (fApplyGainCorrection) {
121  // calculate updated ThresholdIntegral and PeakAmplitudeIntegral applying correction map
122  for (auto iter = sAna_max_amplitude_map.begin(); iter != sAna_max_amplitude_map.end(); iter++) {
123  if (fCalib->fChannelGain.count(iter->first)) {
124  iter->second *= fCalib->fChannelGain[iter->first];
125  }
126  new_PeakAmplitudeIntegral += iter->second;
127  }
128 
129  for (auto iter = sAna_thr_integral_map.begin(); iter != sAna_thr_integral_map.end(); iter++) {
130  if (fCalib->fChannelGain.count(iter->first)) {
131  iter->second *= fCalib->fChannelGain[iter->first];
132  }
133  new_ThresholdIntegral += iter->second;
134  }
135 
136  // update charge value in output event
137  for (int i = 0; i < fSignalEvent->GetNumberOfSignals(); i++) {
138  TRestDetectorSignal* sgn = fSignalEvent->GetSignal(i);
139  if (fCalib->fChannelGain.count(sgn->GetID()) == 0 || fCalib->fChannelGain[sgn->GetID()] == 0) {
140  cout << "warning! unrecorded gain for channel: " << sgn->GetID() << endl;
141  continue;
142  }
143  double gain = fCalib->fChannelGain[sgn->GetID()];
144  for (int j = 0; j < sgn->GetNumberOfPoints(); j++) {
145  auto timecharge = sgn->GetPoint(j);
146  sgn->SetPoint(j, timecharge.X(), timecharge.Y() * gain);
147  }
148  }
149  }
150 
151  SetObservableValue("ChnCorr_AmpInt", new_PeakAmplitudeIntegral);
152  SetObservableValue("ChnCorr_ThrInt", new_ThresholdIntegral);
153 
154  return fSignalEvent;
155 }
156 
158  if (fCreateGainMap) {
159  FitChannelGain();
160  // SaveGainMetadata(fCalibSave);
161  }
162 }
163 
164 void TRestDetectorSingleChannelAnalysisProcess::FitChannelGain() {
165  cout << "TRestDetectorSingleChannelAnalysisProcess: fitting channel gain..." << endl;
166 
167  double channelFitMeanSum = 0;
168 
169  for (auto iter = fChannelThrIntegral.begin(); iter != fChannelThrIntegral.end(); iter++) {
170  TH1D* h = iter->second;
171  if (h->GetEntries() > 100) {
172  // direct fit
173  double middle = (fSpecFitRange.X() + fSpecFitRange.Y()) / 2;
174  double range = (fSpecFitRange.Y() - fSpecFitRange.X()) / 2;
175  TFitResultPtr r = h->Fit("gaus", "QS", "", fSpecFitRange.X(), fSpecFitRange.Y());
176  if (r != -1) {
177  const double* results = r->GetParams();
178  double mean = results[1];
179  double sigma = results[2];
180 
181  if (mean > middle - range / 1.2 && mean < middle + range / 1.2 && sigma / mean < 0.5) {
182  fChannelFitMean[iter->first] = mean;
183  channelFitMeanSum += mean;
184  fChannelGainError[iter->first] = sigma / mean;
185  cout << iter->first << ", mean: " << mean << ", error: " << sigma / mean << endl;
186  continue;
187  }
188  }
189 
190  // if fit with gaus failed, we use TSpectrum to find the peak
191  TSpectrum spc;
192  int n = spc.Search(h);
193  double* peaks = spc.GetPositionX();
194  double min = 1e9;
195  int minpos = 0;
196  for (int i = 0; i < n; i++) {
197  double dist = abs(peaks[i] - middle);
198  if (dist < min) {
199  min = dist;
200  minpos = i;
201  }
202  }
203  if (min < range * 2) {
204  fChannelFitMean[iter->first] = peaks[minpos];
205  channelFitMeanSum += peaks[minpos];
206  fChannelGainError[iter->first] = 1;
207  cout << iter->first << ", peak position: " << peaks[minpos] << ", total peaks: " << n << endl;
208  continue;
209  }
210 
211  // it is very bad channel, we prompt a warning
212  cout << iter->first << ", too bad to fit" << endl;
213  }
214  }
215 
216  double meanmean = channelFitMeanSum / fChannelFitMean.size();
217 
218  cout << meanmean << endl;
219 
220  // normalize and fill the result
221  for (auto iter = fChannelGain.begin(); iter != fChannelGain.end(); iter++) {
222  if (fChannelFitMean.count(iter->first) == 0) {
223  iter->second = 1;
224  } else {
225  iter->second = meanmean / fChannelFitMean[iter->first];
226  }
227  }
228 }
229 
230 /*** This should not be done. The framework saves any metadata structure inside of the
231  * common TRestRun during processing. If TRestRun does not know a particular metadata
232  * instance, then it should be added to the run, and it will be written to disk with it.
233  * If we want just to have a method to export data, in principe it is possible. But
234  * better without using a new TRestRun instance. TRestRun should only be created for
235  * writing the standard processing scheme.
236  ***/
237 void TRestDetectorSingleChannelAnalysisProcess::SaveGainMetadata(string filename) {
238  cout << "TRestDetectorSingleChannelAnalysisProcess: saving result..." << endl;
239 
240  // for (auto iter = fChannelGain.begin(); iter != fChannelGain.end(); iter++) {
241  // cout << iter->first << " " << iter->second << endl;
242  //}
243 
244  fCalib = new TRestDetectorGainMap();
245  fCalib->fChannelGain = fChannelGain;
246  fCalib->SetName("ChannelCalibration");
247 
248  TRestRun* r = (TRestRun*)fRunInfo->Clone();
249  r->SetOutputFileName(filename);
250  r->AddMetadata(fCalib);
251  r->AddMetadata(fReadout);
252  r->AddMetadata(this);
253  r->FormOutputFile();
254 
255  PrintChannelSpectrums(filename);
256  delete r;
257 }
258 
259 TH1D* TRestDetectorSingleChannelAnalysisProcess::GetChannelSpectrum(int id) {
260  if (fChannelThrIntegral.count(id) != 0) return fChannelThrIntegral[id];
261  return nullptr;
262 }
263 
264 void TRestDetectorSingleChannelAnalysisProcess::PrintChannelSpectrums(string filename) {
265  TCanvas* c = new TCanvas();
266  TLatex pText;
267  pText.SetTextColor(kBlack);
268  pText.SetTextFont(132);
269  pText.SetTextSize(0.05);
270  TLine pLine;
271  pLine.SetLineColor(1);
272  pLine.SetLineWidth(1);
273  pLine.SetLineColor(1);
274 
275  c->Print((filename + ".pdf[").c_str());
276  for (auto iter = fChannelThrIntegral.begin(); iter != fChannelThrIntegral.end(); iter++) {
277  if (iter->second != nullptr && iter->second->GetEntries() > 0) {
278  cout << "Drawing: " << iter->first << endl;
279  c->Clear();
280  iter->second->Draw();
281  pText.DrawLatex(fChannelFitMean[iter->first], iter->second->GetMaximum(),
282  Form("Relative gain: %.3f", fChannelGain[iter->first]));
283  pText.DrawLatex(fChannelFitMean[iter->first], iter->second->GetMaximum() * 0.9,
284  Form("Error: %.2f", fChannelGainError[iter->first]));
285  pLine.DrawLine(fChannelFitMean[iter->first], 0, fChannelFitMean[iter->first],
286  iter->second->GetMaximum() * 0.9);
287  c->Print((filename + ".pdf").c_str());
288  }
289  }
290  c->Clear();
291  c->Print((filename + ".pdf]").c_str());
292  delete c;
293 }
294 
295 // setting amplification:
296 // <parameter name="modulesAmp" value = "2-1:5-1.2:6-0.8:8-0.9" />
297 // setting readout modules to draw:
298 // <parameter name="modulesHist" value="2:5:6:8"/>
300  string mode = GetParameter("mode", "apply");
301  if (mode == "create") {
302  fCreateGainMap = true;
303  fApplyGainCorrection = false;
304  fSingleThreadOnly = true;
305  } else if (mode == "apply") {
306  fCreateGainMap = false;
307  fApplyGainCorrection = true;
308  fSingleThreadOnly = false;
309  } else {
310  RESTError << "illegal mode definition! supported: create, apply" << RESTendl;
311  abort();
312  }
313 
314  fThrIntegralCutRange = StringTo2DVector(GetParameter("thrEnergyRange", "(0,1e9)"));
315  fNGoodSignalsCutRange = StringTo2DVector(GetParameter("nGoodSignalsRange", "(4,14)"));
316  fSpecFitRange = StringTo2DVector(GetParameter("specFitRange", "(1e4,2e4)"));
317  fCalibSave = GetParameter("save", "calib.root");
318 }
void SetPoint(TVector2 p)
If the point already exists inside the detector signal event, it will be overwritten....
TRestEvent * ProcessEvent(TRestEvent *inputEvent) override
Process one event.
void EndProcess() override
To be executed at the end of the run (outside event loop)
void InitProcess() override
To be executed at the beginning of the run (outside event loop)
void InitFromConfigFile() override
To make settings from rml file. This method must be implemented in the derived class.
A base class for any REST event.
Definition: TRestEvent.h:38
Data provider and manager in REST.
Definition: TRestRun.h:18
TFile * FormOutputFile()
Create a new TFile as REST output file. Writing metadata objects into it.
Definition: TRestRun.cxx:1036
void AddMetadata(TRestMetadata *meta)
add metadata object to the metadata list
Definition: TRestRun.h:112
TVector2 StringTo2DVector(std::string in)
Gets a 2D-vector from a string.