REST-for-Physics  v2.3
Rare Event Searches ToolKit for Physics
TRestDataSetGainMap.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 https://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 https://www.gnu.org/licenses/. *
20  * For the list of contributors see $REST_PATH/CREDITS. *
21  *************************************************************************/
22 
55 
137 
138 #include "TRestDataSetGainMap.h"
139 
140 ClassImp(TRestDataSetGainMap);
145 
160 TRestDataSetGainMap::TRestDataSetGainMap(const char* configFilename, std::string name)
161  : TRestMetadata(configFilename) {
164 }
165 
170 
171 void TRestDataSetGainMap::Initialize() { SetSectionName(this->ClassName()); }
177  this->Initialize();
179 
180  // Load Module from RML
181  TiXmlElement* moduleDefinition = GetElement("module");
182  while (moduleDefinition != nullptr) {
183  fModulesCal.push_back(Module(*this));
184  fModulesCal.back().LoadConfigFromTiXmlElement(moduleDefinition);
185 
186  moduleDefinition = GetNextElement(moduleDefinition);
187  }
188 
189  fCut = (TRestCut*)InstantiateChildMetadata("TRestCut");
190 
192 }
193 
198  for (auto& mod : fModulesCal) {
199  RESTInfo << "Generating gain map of plane " << mod.GetPlaneId() << " module " << mod.GetModuleId()
200  << RESTendl;
201  mod.GenerateGainMap();
203  mod.DrawSpectrum();
204  mod.DrawFullSpectrum();
205  mod.DrawGainMap();
206  }
207  }
208 }
209 
222 void TRestDataSetGainMap::CalibrateDataSet(const std::string& dataSetFileName, std::string outputFileName,
223  std::vector<std::string> excludeColumns) {
224  if (fModulesCal.empty()) {
225  RESTError << "TRestDataSetGainMap::CalibrateDataSet: No modules defined." << RESTendl;
226  return;
227  }
228 
229  TRestDataSet dataSet;
230  dataSet.EnableMultiThreading(true);
231  dataSet.Import(dataSetFileName);
232  auto dataFrame = dataSet.GetDataFrame();
233 
234  // Define a new column with the identifier (pmID) of the module for each row (event)
235  std::string pmIDname = (std::string)GetName() + "_pmID";
236  std::string modCut = fModulesCal[0].GetModuleDefinitionCut();
237  int pmID = fModulesCal[0].GetPlaneId() * 10 + fModulesCal[0].GetModuleId();
238 
239  auto columnList = dataFrame.GetColumnNames();
240  if (std::find(columnList.begin(), columnList.end(), pmIDname) == columnList.end())
241  dataFrame = dataFrame.Define(pmIDname, modCut + " ? " + std::to_string(pmID) + " : -1");
242  else
243  dataFrame = dataFrame.Redefine(pmIDname, modCut + " ? " + std::to_string(pmID) + " : -1");
244 
245  for (size_t n = 1; n < fModulesCal.size(); n++) {
246  modCut = fModulesCal[n].GetModuleDefinitionCut();
247  pmID = fModulesCal[n].GetPlaneId() * 10 + fModulesCal[n].GetModuleId();
248  dataFrame = dataFrame.Redefine(pmIDname, (modCut + " ? " + std::to_string(pmID) + " : " + pmIDname));
249  }
250 
251  // Define a new column with the calibrated observable
252  auto calibrate = [this](double val, double x, double y, int pmID) {
253  for (auto& m : fModulesCal) {
254  if (pmID == m.GetPlaneId() * 10 + m.GetModuleId())
255  return m.GetSlope(x, y) * val + m.GetIntercept(x, y);
256  }
257  // RESTError << "TRestDataSetGainMap::CalibrateDataSet: Module not found for pmID " << pmID <<
258  // RESTendl;
259  return std::numeric_limits<double>::quiet_NaN();
260  };
261  std::string calibObsName = (std::string)GetName() + "_";
262  calibObsName += GetObservable().erase(0, GetObservable().find("_") + 1); // remove the "rawAna_" part
263  dataFrame = dataFrame.Define(calibObsName, calibrate,
265 
266  // Define a new column with the calibrated observable for the whole module calibration
267  auto calibrateFullSpc = [this](double val, int pmID) {
268  for (auto& m : fModulesCal) {
269  if (pmID == m.GetPlaneId() * 10 + m.GetModuleId())
270  return m.GetSlopeFullSpc() * val + m.GetInterceptFullSpc();
271  }
272  // RESTError << "TRestDataSetGainMap::CalibrateDataSet: Module not found for pmID " << pmID <<
273  // RESTendl;
274  return std::numeric_limits<double>::quiet_NaN();
275  };
276  std::string calibObsNameFullSpc = (std::string)GetName() + "_";
277  calibObsNameFullSpc +=
278  GetObservable().erase(0, GetObservable().find("_") + 1); // remove the "rawAna_" part
279  calibObsNameFullSpc += "_NoSegmentation";
280  dataFrame = dataFrame.Define(calibObsNameFullSpc, calibrateFullSpc, {fObservable, pmIDname});
281 
282  dataSet.SetDataFrame(dataFrame);
283 
284  // Format the output file name and export the dataSet
285  if (outputFileName.empty()) outputFileName = dataSetFileName;
286  if (outputFileName == dataSetFileName) { // TRestDataSet cannot be overwritten
287  std::string gmName = GetName();
288  outputFileName = outputFileName.substr(0, outputFileName.find_last_of(".")); // remove extension
289  outputFileName += "_" + gmName;
290  outputFileName += TRestTools::GetFileNameExtension(dataSetFileName);
291  }
292 
293  // Export dataset. Exclude columns if requested.
294  std::set<std::string> excludeCol; // vector with the explicit column names to be excluded
295  auto columns = dataSet.GetDataFrame().GetColumnNames();
296  // Get the columns to be excluded from the list of columns. It accepts wildcards "*" and "?"
297  for (auto& eC : excludeColumns) {
298  if (eC.find("*") != std::string::npos || eC.find("?") != std::string::npos) {
299  for (auto& c : columns)
300  if (MatchString(c, eC)) excludeCol.insert(c);
301  } else if (std::find(columns.begin(), columns.end(), eC) != columns.end())
302  excludeCol.insert(eC);
303  }
304  // Remove the calibObsName, calibObsNameFullSpc and pmIDname from the list of columns to be excluded
305  excludeCol.erase(calibObsName);
306  excludeCol.erase(calibObsNameFullSpc);
307  excludeCol.erase(pmIDname);
308 
309  RESTDebug << "Excluding columns: ";
310  for (auto& c : excludeCol) RESTDebug << c << ", ";
311  RESTDebug << RESTendl;
312 
313  dataSet.Export(outputFileName, std::vector<std::string>(excludeCol.begin(), excludeCol.end()));
314 
315  // Add this TRestDataSetGainMap metadata to the output file
316  TFile* f = TFile::Open(outputFileName.c_str(), "UPDATE");
317  this->Write();
318  f->Close();
319  delete f;
320 }
321 
327  if (index < fModulesCal.size()) return &fModulesCal[index];
328 
329  RESTError << "No ModuleCalibration with index " << index;
330  if (fModulesCal.empty())
331  RESTError << ". There are no modules defined." << RESTendl;
332  else
333  RESTError << ". Max index is " << fModulesCal.size() - 1 << RESTendl;
334  return nullptr;
335 }
336 
341 TRestDataSetGainMap::Module* TRestDataSetGainMap::GetModule(const int planeID, const int moduleID) {
342  for (auto& i : fModulesCal) {
343  if (i.GetPlaneId() == planeID && i.GetModuleId() == moduleID) return &i;
344  }
345  RESTError << "No ModuleCalibration with planeID " << planeID << " and moduleID " << moduleID << RESTendl;
346  return nullptr;
347 }
348 
354 double TRestDataSetGainMap::GetSlopeParameter(const int planeID, const int moduleID, const double x,
355  const double y) {
356  Module* moduleCal = GetModule(planeID, moduleID);
357  if (moduleCal == nullptr) return 0; // return numeric_limits<double>::quiet_NaN()
358  return moduleCal->GetSlope(x, y);
359 }
360 
365 double TRestDataSetGainMap::GetSlopeParameterFullSpc(const int planeID, const int moduleID) {
366  Module* moduleCal = GetModule(planeID, moduleID);
367  if (moduleCal == nullptr) return 0; // return numeric_limits<double>::quiet_NaN()
368  return moduleCal->GetSlopeFullSpc();
369 }
370 
376 double TRestDataSetGainMap::GetInterceptParameter(const int planeID, const int moduleID, const double x,
377  const double y) {
378  Module* moduleCal = GetModule(planeID, moduleID);
379  if (moduleCal == nullptr) return 0; // return numeric_limits<double>::quiet_NaN()
380  return moduleCal->GetIntercept(x, y);
381 }
382 
387 double TRestDataSetGainMap::GetInterceptParameterFullSpc(const int planeID, const int moduleID) {
388  Module* moduleCal = GetModule(planeID, moduleID);
389  if (moduleCal == nullptr) return 0; // return numeric_limits<double>::quiet_NaN()
390  return moduleCal->GetInterceptFullSpc();
391 }
392 
397 std::set<int> TRestDataSetGainMap::GetPlaneIDs() const {
398  std::set<int> planeIDs;
399  for (const auto& mc : fModulesCal) planeIDs.insert(mc.GetPlaneId());
400  return planeIDs;
401 }
402 
407 std::set<int> TRestDataSetGainMap::GetModuleIDs(const int planeId) const {
408  std::set<int> moduleIDs;
409  for (const auto& mc : fModulesCal)
410  if (mc.GetPlaneId() == planeId) moduleIDs.insert(mc.GetModuleId());
411  return moduleIDs;
412 }
413 
417 std::map<int, std::set<int>> TRestDataSetGainMap::GetModuleIDs() const {
418  std::map<int, std::set<int>> moduleIds;
419  for (const int planeId : GetPlaneIDs())
420  moduleIds.insert(std::pair<int, std::set<int>>(planeId, GetModuleIDs(planeId)));
421  return moduleIds;
422 }
423 
424 TRestDataSetGainMap& TRestDataSetGainMap::operator=(TRestDataSetGainMap& src) {
425  SetName(src.GetName());
426  fOutputFileName = src.GetOutputFileName();
427  fObservable = src.GetObservable();
428  fSpatialObservableX = src.GetSpatialObservableX();
429  fSpatialObservableY = src.GetSpatialObservableY();
430  fCut = src.GetCut();
431  fModulesCal.clear();
432  for (auto pID : src.GetPlaneIDs())
433  for (auto mID : src.GetModuleIDs(pID)) fModulesCal.push_back(*src.GetModule(pID, mID));
434  return *this;
435 }
436 
441 void TRestDataSetGainMap::SetModule(const Module& moduleCal) {
442  for (auto& i : fModulesCal) {
443  if (i.GetPlaneId() == moduleCal.GetPlaneId() && i.GetModuleId() == moduleCal.GetModuleId()) {
444  i = moduleCal;
445  return;
446  }
447  }
448  fModulesCal.push_back(moduleCal);
449 }
450 
455 void TRestDataSetGainMap::Import(const std::string& fileName) {
456  if (fileName.empty()) {
457  RESTError << "No input calibration file defined" << RESTendl;
458  return;
459  }
460 
461  if (TRestTools::isRootFile(fileName)) {
462  RESTInfo << "Opening " << fileName << RESTendl;
463  TFile* f = TFile::Open(fileName.c_str(), "READ");
464  if (f == nullptr) {
465  RESTError << "Cannot open calibration file " << fileName << RESTendl;
466  return;
467  }
468 
469  TRestDataSetGainMap* cal = nullptr;
470  if (f != nullptr) {
471  TIter nextkey(f->GetListOfKeys());
472  TKey* key;
473  while ((key = (TKey*)nextkey())) {
474  std::string kName = key->GetClassName();
475  if (REST_Reflection::GetClassQuick(kName.c_str()) != nullptr &&
476  REST_Reflection::GetClassQuick(kName.c_str())->InheritsFrom("TRestDataSetGainMap")) {
477  cal = f->Get<TRestDataSetGainMap>(key->GetName());
478  *this = *cal;
479  }
480  }
481  }
482  } else
483  RESTError << "File extension not supported for " << fileName << RESTendl;
485 }
486 
493 void TRestDataSetGainMap::Export(const std::string& fileName) {
494  if (!fileName.empty()) fOutputFileName = fileName;
495  if (fOutputFileName.empty()) {
496  RESTError << "No output file defined" << RESTendl;
497  return;
498  }
499 
501  TFile* f = TFile::Open(fOutputFileName.c_str(), "UPDATE");
502  this->Write(GetName());
503  f->Close();
504  delete f;
505  RESTInfo << "Calibration saved to " << fOutputFileName << RESTendl;
506  } else
507  RESTError << "File extension for " << fOutputFileName << "is not supported." << RESTendl;
508 }
509 
515  RESTMetadata << " Calibration dataset: " << fCalibFileName << RESTendl;
516  if (fCut) {
517  RESTMetadata << " Cuts applied: ";
518  /* print only cutStrings and paramCut because
519  TRestDataSet::MakeCut() uses fCut->GetCutStrings() and fCut->GetParamCut() */
520  for (const auto& cut : fCut->GetCutStrings()) RESTMetadata << cut << ", " << RESTendl;
521  for (const auto& cut : fCut->GetParamCut())
522  RESTMetadata << cut.first << " " << cut.second << ", " << RESTendl;
523  }
524  RESTMetadata << " Output file: " << fOutputFileName << RESTendl;
525  RESTMetadata << RESTendl;
526  RESTMetadata << " Number of planes: " << GetNumberOfPlanes() << RESTendl;
527  RESTMetadata << " Number of modules: " << GetNumberOfModules() << RESTendl;
528  RESTMetadata << " Calibration observable: " << fObservable << RESTendl;
529  RESTMetadata << " Spatial observable X: " << fSpatialObservableX << RESTendl;
530  RESTMetadata << " Spatial observable Y: " << fSpatialObservableY << RESTendl;
531  RESTMetadata << "-----------------------------------------------" << RESTendl;
532  for (auto& i : fModulesCal) i.Print();
533  RESTMetadata << "***********************************************" << RESTendl;
534  RESTMetadata << RESTendl;
535 }
536 
544 std::pair<int, int> TRestDataSetGainMap::Module::GetIndexMatrix(const double x, const double y) const {
545  int index_x = -1, index_y = -1;
546 
547  if (fSplitX.upper_bound(x) != fSplitX.end()) {
548  index_x = std::distance(fSplitX.begin(), fSplitX.upper_bound(x)) - 1;
549  if (index_x < 0) {
550  RESTWarning << "index_x < 0 for x = " << x << " and fSplitX[0]=" << *fSplitX.begin()
551  << p->RESTendl;
552  index_x = 0;
553  }
554  } else {
555  RESTWarning << "x is out of split for x = " << x << p->RESTendl;
556  index_x = fSplitX.size() - 2;
557  }
558 
559  if (fSplitY.upper_bound(y) != fSplitY.end()) {
560  index_y = std::distance(fSplitY.begin(), fSplitY.upper_bound(y)) - 1;
561  if (index_y < 0) {
562  RESTWarning << "index_y < 0 for y = " << y << " and fSplitY[0]=" << *fSplitY.begin()
563  << p->RESTendl;
564  index_y = 0;
565  }
566  } else {
567  RESTWarning << "y is out of split for y = " << y << p->RESTendl;
568  index_y = fSplitY.size() - 2;
569  }
570 
571  return std::make_pair(index_x, index_y);
572 }
580 double TRestDataSetGainMap::Module::GetSlope(const double x, const double y) const {
581  auto [index_x, index_y] = GetIndexMatrix(x, y);
582  if (fSlope.empty()) {
583  RESTError << "Calibration slope matrix is empty. Returning 0" << p->RESTendl;
584  return 0;
585  }
586 
587  if (index_x > (int)fSlope.size() || index_y > (int)fSlope.at(0).size()) {
588  RESTError << "Index out of range. Returning 0" << p->RESTendl;
589  return 0;
590  }
591 
592  return fSlope[index_x][index_y];
593 }
594 
602 double TRestDataSetGainMap::Module::GetIntercept(const double x, const double y) const {
603  auto [index_x, index_y] = GetIndexMatrix(x, y);
604  if (fIntercept.empty()) {
605  RESTError << "Calibration constant matrix is empty. Returning 0" << p->RESTendl;
606  return 0;
607  }
608 
609  if (index_x > (int)fIntercept.size() || index_y > (int)fIntercept.at(0).size()) {
610  RESTError << "Index out of range. Returning 0" << p->RESTendl;
611  return 0;
612  }
613 
614  return fIntercept[index_x][index_y];
615 }
616 
621  SetSplitX();
622  SetSplitY();
623 }
631  if (fNumberOfSegmentsX < 1) {
632  RESTError << "SetSplitX: fNumberOfSegmentsX must be >=1." << p->RESTendl;
633  return;
634  }
635  std::set<double> split;
636  for (int i = 0; i <= fNumberOfSegmentsX; i++) { // <= so that the last segment is included
637  double x =
638  fReadoutRange.X() + ((fReadoutRange.Y() - fReadoutRange.X()) / (float)fNumberOfSegmentsX) * i;
639  split.insert(x);
640  }
641  SetSplitX(split);
642 }
643 
644 void TRestDataSetGainMap::Module::SetSplitX(const std::set<double>& splitX) {
645  if (splitX.size() < 2) {
646  RESTError << "SetSplitX: split size must be >=2 (start and end of range must be included)."
647  << p->RESTendl;
648  return;
649  }
650  if (!fSlope.empty())
651  RESTWarning << "SetSplitX: changing split but current gain map and calibration paremeters correspond "
652  "to previous splitting. Use GenerateGainMap() to update them."
653  << p->RESTendl;
654  fSplitX = splitX;
655  fNumberOfSegmentsX = fSplitX.size() - 1;
656 }
664  if (fNumberOfSegmentsY < 1) {
665  RESTError << "SetSplitY: fNumberOfSegmentsY must be >=1." << p->RESTendl;
666  return;
667  }
668  std::set<double> split;
669  for (int i = 0; i <= fNumberOfSegmentsY; i++) { // <= so that the last segment is included
670  double y =
671  fReadoutRange.X() + ((fReadoutRange.Y() - fReadoutRange.X()) / (float)fNumberOfSegmentsY) * i;
672  split.insert(y);
673  }
674  SetSplitY(split);
675 }
676 
677 void TRestDataSetGainMap::Module::SetSplitY(const std::set<double>& splitY) {
678  if (splitY.size() < 2) {
679  RESTError << "SetSplitY: split size must be >=2 (start and end of range must be included)."
680  << p->RESTendl;
681  return;
682  }
683  if (!fSlope.empty())
684  RESTWarning << "SetSplitY: changing split but current gain map and calibration paremeters correspond "
685  "to previous splitting. Use GenerateGainMap() to update them."
686  << p->RESTendl;
687  fSplitY = splitY;
688  fNumberOfSegmentsY = fSplitY.size() - 1;
689 }
690 
708  //--- Initial checks and settings ---
709  std::string dsFileName = fDataSetFileName;
710  if (dsFileName.empty()) dsFileName = p->GetCalibrationFileName();
711  if (dsFileName.empty()) {
712  RESTError << "No calibration file defined" << p->RESTendl;
713  return;
714  }
715 
716  if (!TRestTools::fileExists(dsFileName)) {
717  RESTError << "Calibration file " << dsFileName << " does not exist." << p->RESTendl;
718  return;
719  }
720  if (!TRestTools::isDataSet(dsFileName)) RESTWarning << dsFileName << " is not a dataset." << p->RESTendl;
721  TRestDataSet dataSet;
722  dataSet.EnableMultiThreading(true);
723  dataSet.Import(dsFileName);
724  fDataSetFileName = dsFileName;
725 
726  dataSet.SetDataFrame(dataSet.MakeCut(p->GetCut()));
727 
728  if (fSplitX.empty()) SetSplitX();
729  if (fSplitY.empty()) SetSplitY();
730 
731  //--- Get the calibration range if not provided (default is 0,0) ---
732  if (fCalibRange.X() >= fCalibRange.Y()) {
733  // Get spectrum for this file
734  std::string cut = fDefinitionCut;
735  if (cut.empty()) cut = "1";
736  auto histo = dataSet.GetDataFrame().Filter(cut).Histo1D({"temp", "", fNBins, 0, 0}, GetObservable());
737  std::unique_ptr<TH1F> hpunt = std::unique_ptr<TH1F>(static_cast<TH1F*>(histo->Clone()));
738  // double xMin = hpunt->GetXaxis()->GetXmin();
739  double xMax = hpunt->GetXaxis()->GetXmax();
740 
741  // Reduce the range to avoid the possible empty (nCounts<1%) end part of the spectrum
742  double fraction = 1, nAtEndSpc = 0, step = 0.66;
743  while (nAtEndSpc * 1. / hpunt->Integral() < 0.01 && fraction > 0.001) {
744  fraction *= step;
745  nAtEndSpc = hpunt->Integral(hpunt->FindFixBin(hpunt->GetXaxis()->GetXmax() * fraction),
746  hpunt->FindFixBin(hpunt->GetXaxis()->GetXmax()));
747  }
748  xMax = hpunt->GetXaxis()->GetXmax() * fraction /
749  step; // previous step is the last one that nAtEndSpc<1%
750  // Set the calibration range if needed
751  // fCalibRange.SetX(xMin);
752  fCalibRange.SetY(xMax);
753  hpunt.reset(); // delete hpunt;
754  RESTDebug << "Calibration range (auto)set to (" << fCalibRange.X() << "," << fCalibRange.Y() << ")"
755  << p->RESTendl;
756  }
757 
758  // --- Definition of histogram whole module ---
759  std::string hModuleName = "hSpc_" + std::to_string(fPlaneId) + "_" + std::to_string(fModuleId);
760  delete fFullSpectrum;
761  fFullSpectrum = new TH1F(hModuleName.c_str(), "", fNBins, fCalibRange.X(), fCalibRange.Y());
762 
763  // build the spectrum for the whole module
764  std::string cut = fDefinitionCut;
765  if (cut.empty()) cut = "1";
766  auto histoMod = dataSet.GetDataFrame().Filter(cut).Histo1D(
767  {"tempMod", "", fNBins, fCalibRange.X(), fCalibRange.Y()}, GetObservable());
768  std::unique_ptr<TH1F> hpuntMod = std::unique_ptr<TH1F>(static_cast<TH1F*>(histoMod->Clone()));
769  fFullSpectrum->Add(hpuntMod.get());
770 
771  //--- Definition of histogram matrix ---
772  std::vector<std::vector<TH1F*>> h(fNumberOfSegmentsX, std::vector<TH1F*>(fNumberOfSegmentsY, nullptr));
773  for (size_t i = 0; i < h.size(); i++) {
774  for (size_t j = 0; j < h.at(0).size(); j++) {
775  std::string name = hModuleName + "_" + std::to_string(i) + "_" + std::to_string(j);
776  h[i][j] = new TH1F(name.c_str(), "", fNBins, fCalibRange.X(),
777  fCalibRange.Y()); // h[column][row] equivalent to h[x][y]
778  }
779  }
780 
781  // build the spectrum for each segment
782  auto itX = fSplitX.begin();
783  for (size_t i = 0; i < h.size(); i++) {
784  auto itY = fSplitY.begin();
785  for (size_t j = 0; j < h.at(0).size(); j++) {
786  // Get the segment limits from the splits
787  auto xLower = *itX;
788  auto xUpper = *std::next(itX);
789  auto yLower = *itY;
790  auto yUpper = *std::next(itY);
791 
792  std::string segment_cut = "";
793  if (!GetSpatialObservableX().empty())
794  segment_cut += GetSpatialObservableX() + ">=" + std::to_string(xLower) + "&&" +
795  GetSpatialObservableX() + "<" + std::to_string(xUpper);
796  if (!GetSpatialObservableY().empty())
797  segment_cut += "&&" + GetSpatialObservableY() + ">=" + std::to_string(yLower) + "&&" +
798  GetSpatialObservableY() + "<" + std::to_string(yUpper);
799  if (!fDefinitionCut.empty()) segment_cut += "&&" + fDefinitionCut;
800  if (segment_cut.empty()) segment_cut = "1";
801  RESTExtreme << "Segment[" << i << "][" << j << "] cut: " << segment_cut << p->RESTendl;
802  auto histo = dataSet.GetDataFrame()
803  .Filter(segment_cut)
804  .Histo1D({"temp", "", h[i][j]->GetNbinsX(), h[i][j]->GetXaxis()->GetXmin(),
805  h[i][j]->GetXaxis()->GetXmax()},
806  GetObservable());
807  std::unique_ptr<TH1F> hpunt = std::unique_ptr<TH1F>(static_cast<TH1F*>(histo->Clone()));
808  h[i][j]->Add(hpunt.get());
809  hpunt.reset(); // delete hpunt;
810  itY++;
811  }
812  itX++;
813  }
814 
815  //--- Fit every peak energy for every segment ---
816  std::vector<std::vector<double>> calParSlope(fNumberOfSegmentsX,
817  std::vector<double>(fNumberOfSegmentsY, 0));
818  std::vector<std::vector<double>> calParIntercept(fNumberOfSegmentsX,
819  std::vector<double>(fNumberOfSegmentsY, 0));
820  fSegLinearFit = std::vector(h.size(), std::vector<TGraph*>(h.at(0).size(), nullptr));
821  for (size_t i = 0; i < h.size(); i++)
822  for (int j = 0; j < (int)h.at(0).size(); j++) {
823  fSegLinearFit[i][j] = new TGraph();
824  auto [intercept, slope] = FitPeaks(h[i][j], fSegLinearFit[i][j]);
825  calParSlope[i][j] = slope;
826  calParIntercept[i][j] = intercept;
827  }
828  fSlope = calParSlope;
829  fIntercept = calParIntercept;
830  fSegSpectra = h;
831 
832  //--- Fit every peak energy for the whole module ---
833  delete fFullLinearFit;
834  fFullLinearFit = new TGraph();
835  auto [intercept, slope] = FitPeaks(fFullSpectrum, fFullLinearFit);
836  fFullSlope = slope;
837  fFullIntercept = intercept;
838 }
839 
840 std::pair<double, double> TRestDataSetGainMap::Module::FitPeaks(TH1F* hSeg, TGraph* gr) {
841  if (!hSeg) {
842  RESTError << "No histogram for fitting" << p->RESTendl;
843  return std::make_pair(0, 0);
844  }
845  if (hSeg->Integral() == 0) {
846  RESTError << "Empty spectrum " << hSeg->GetName() << p->RESTendl;
847  return std::make_pair(0, 0);
848  }
849  std::shared_ptr<TGraph> graph = std::shared_ptr<TGraph>(new TGraph());
850  RESTExtreme << "Fitting peaks for " << hSeg->GetName() << p->RESTendl;
851 
852  // Search for peaks --> peakPos
853  std::unique_ptr<TSpectrum> s(new TSpectrum(2 * fEnergyPeaks.size() + 1));
854  std::vector<double> peakPos;
855  s->Search(hSeg, 2, "goff", 0.1);
856  for (int k = 0; k < s->GetNPeaks(); k++) peakPos.push_back(s->GetPositionX()[k]);
857  std::sort(peakPos.begin(), peakPos.end(), std::greater<double>());
858  const double ratio =
859  peakPos.size() == 0 ? 1 : peakPos.front() / fEnergyPeaks.front(); // to estimate peak position
860 
861  // Initialize graph for linear fit
862  graph->SetName("grFit");
863  graph->SetTitle((";" + GetObservable() + ";energy").c_str());
864 
865  // Fit every energy peak
866  int c = 0;
867  double mu = 0;
868  for (const auto& energy : fEnergyPeaks) {
869  RESTExtreme << "\t fitting energy " << DoubleToString(energy, "%g") << p->RESTendl;
870  // estimation of the peak position is between start and end
871  double pos = energy * ratio;
872  double start = pos * 0.8;
873  double end = pos * 1.2;
874  if (fRangePeaks.at(c).X() < fRangePeaks.at(c).Y()) { // if range is defined use it
875  start = fRangePeaks.at(c).X();
876  end = fRangePeaks.at(c).Y();
877  }
878 
879  do {
880  if (fAutoRangePeaks) {
881  if (peakPos.size() > 0) {
882  // Find the peak position that is between start and end
883  pos = peakPos.at(0);
884  while (!(start < pos && pos < end)) {
885  // if none of the peak position is
886  // between start and end, use the greater one.
887  if (pos == peakPos.back()) {
888  pos = peakPos.at(0);
889  break;
890  }
891  pos = *std::next(std::find(peakPos.begin(), peakPos.end(),
892  pos)); // get next peak position
893  }
894  peakPos.erase(std::find(peakPos.begin(), peakPos.end(),
895  pos)); // remove this peak position from the list
896  // final estimation of the peak range (idem fitting window) with this peak
897  // position pos
898  start = pos * 0.8;
899  end = pos * 1.2;
900  const double relDist = peakPos.size() > 0 ? (pos - peakPos.front()) / pos : 999;
901  if (relDist < 0.2) { // if the next peak is too close reduce the window width
902  start = pos * (1 - relDist / 2);
903  end = pos * (1 + relDist / 2);
904  }
905  }
906  }
907 
908  std::string name = "g" + std::to_string(c);
909  TF1* g = new TF1(name.c_str(), "gaus", start, end);
910  RESTExtreme << "\t\tat " << DoubleToString(pos, "%.3g") << ". Range("
911  << DoubleToString(start, "%.3g") << ", " << DoubleToString(end, "%.3g") << ")"
912  << p->RESTendl;
913 
914  if (hSeg->GetFunction(name.c_str())) // remove previous fit
915  hSeg->GetListOfFunctions()->Remove(hSeg->GetFunction(name.c_str()));
916 
917  hSeg->Fit(g, "R+Q0"); // use 0 to not draw the fit but save it
918  mu = g->GetParameter(1);
919  RESTExtreme << "\t\tgaus mean " << DoubleToString(mu, "%g") << p->RESTendl;
920  } while (fAutoRangePeaks && peakPos.size() > 0 &&
921  !(start < mu && mu < end)); // avoid small peaks on main peak tail
922  graph->SetPoint(c++, mu, energy);
923  }
924  s.reset(); // delete s;
925 
926  if (fZeroPoint) graph->SetPoint(c++, 0, 0);
927  while (graph->GetN() < 2) { // minimun 2 points needed for linear fit
928  graph->SetPoint(c++, 0, 0);
929  SetZeroPoint(true);
930  RESTDebug << "Not enough points for linear fit. Adding and setting zero point to true" << p->RESTendl;
931  }
932 
933  // Linear fit
934  std::unique_ptr<TF1> linearFit;
935  linearFit = std::unique_ptr<TF1>(new TF1("linearFit", "pol1"));
936  graph->Fit("linearFit", "SQ"); // Q for quiet mode
937 
938  if (gr) *gr = *(TGraph*)graph->Clone(); // if nullptr is passed, do not copy the graph
939  return std::make_pair(linearFit->GetParameter(0), linearFit->GetParameter(1));
940 }
941 
949 void TRestDataSetGainMap::Module::Refit(const TVector2& position, const double energyPeak,
950  const TVector2& range) {
951  auto [index_x, index_y] = GetIndexMatrix(position.X(), position.Y());
952  int peakNumber = -1;
953  for (size_t i = 0; i < fEnergyPeaks.size(); i++)
954  if (fEnergyPeaks.at(i) == energyPeak) {
955  peakNumber = i;
956  break;
957  }
958  if (peakNumber == -1) {
959  RESTError << "Energy " << energyPeak << " not found in the list of energy peaks" << p->RESTendl;
960  return;
961  }
962  Refit((size_t)index_x, (size_t)index_y, (size_t)peakNumber, range);
963 }
964 
974 void TRestDataSetGainMap::Module::Refit(const size_t x, const size_t y, const size_t peakNumber,
975  const TVector2& range) {
976  if (fSegSpectra.empty()) {
977  RESTError << "No gain map found. Use GenerateGainMap() first." << p->RESTendl;
978  return;
979  }
980  if (x >= fSegSpectra.size() || y >= fSegSpectra.at(0).size()) {
981  RESTError << "Segment with index (" << x << ", " << y << ") not found" << p->RESTendl;
982  return;
983  }
984  if (peakNumber >= fEnergyPeaks.size()) {
985  RESTError << "Peak with index " << peakNumber << " not found" << p->RESTendl;
986  return;
987  }
988 
989  // Refit the desired peak
990  std::string name = "g" + std::to_string(peakNumber);
991  TF1* g = new TF1(name.c_str(), "gaus", range.X(), range.Y());
992  TH1F* h = fSegSpectra.at(x).at(y);
993  while (h->GetFunction(name.c_str())) // clear previous fits for this peakNumber
994  h->GetListOfFunctions()->Remove(h->GetFunction(name.c_str()));
995  h->Fit(g, "R+Q0"); // use 0 to not draw the fit but save it
996 
997  // Change the point of the graph
998  UpdateCalibrationFits(x, y);
999 }
1000 
1008 void TRestDataSetGainMap::Module::RefitFullSpc(const double energyPeak, const TVector2& range) {
1009  int peakNumber = -1;
1010  for (size_t i = 0; i < fEnergyPeaks.size(); i++)
1011  if (fEnergyPeaks.at(i) == energyPeak) {
1012  peakNumber = i;
1013  break;
1014  }
1015  if (peakNumber == -1) {
1016  RESTError << "Energy " << energyPeak << " not found in the list of energy peaks" << p->RESTendl;
1017  return;
1018  }
1019  RefitFullSpc((size_t)peakNumber, range);
1020 }
1021 
1029 void TRestDataSetGainMap::Module::RefitFullSpc(const size_t peakNumber, const TVector2& range) {
1030  if (!fFullSpectrum) {
1031  RESTError << "No gain map found. Use GenerateGainMap() first." << p->RESTendl;
1032  return;
1033  }
1034  if (peakNumber >= fEnergyPeaks.size()) {
1035  RESTError << "Peak with index " << peakNumber << " not found" << p->RESTendl;
1036  return;
1037  }
1038 
1039  // Refit the desired peak
1040  std::string name = "g" + std::to_string(peakNumber);
1041  TF1* g = new TF1(name.c_str(), "gaus", range.X(), range.Y());
1042  while (fFullSpectrum->GetFunction(name.c_str())) // clear previous fits for this peakNumber
1043  fFullSpectrum->GetListOfFunctions()->Remove(fFullSpectrum->GetFunction(name.c_str()));
1044  fFullSpectrum->Fit(g, "R+Q0"); // use 0 to not draw the fit but save it
1045 
1046  // Change the point of the graph
1047  UpdateCalibrationFitsFullSpc();
1048 }
1049 
1058 void TRestDataSetGainMap::Module::UpdateCalibrationFits(const size_t x, const size_t y) {
1059  if (fSegSpectra.empty()) {
1060  RESTError << "No gain map found. Use GenerateGainMap() first." << p->RESTendl;
1061  return;
1062  }
1063  if (x >= fSegSpectra.size() || y >= fSegSpectra.at(0).size()) {
1064  RESTError << "Segment with index (" << x << ", " << y << ") not found" << p->RESTendl;
1065  return;
1066  }
1067 
1068  TH1F* h = fSegSpectra.at(x).at(y);
1069  TGraph* gr = fSegLinearFit.at(x).at(y);
1070 
1071  auto [intercept, slope] = UpdateCalibrationFits(h, gr);
1072  fSlope[x][y] = slope;
1073  fIntercept[x][y] = intercept;
1074 }
1075 
1076 std::pair<double, double> TRestDataSetGainMap::Module::UpdateCalibrationFits(TH1F* h, TGraph* gr) {
1077  if (!h) {
1078  RESTError << "No histogram for updating fits" << p->RESTendl;
1079  return std::make_pair(0, 0);
1080  }
1081  if (!gr) {
1082  RESTError << "No graph for updating fits" << p->RESTendl;
1083  return std::make_pair(0, 0);
1084  }
1085  if (h->Integral() == 0) {
1086  RESTError << "Empty spectrum " << h->GetName() << p->RESTendl;
1087  return std::make_pair(0, 0);
1088  }
1089 
1090  // Clear the points of the graph
1091  for (size_t i = 0; i < fEnergyPeaks.size(); i++) gr->RemovePoint(i);
1092  // Add the new points to the graph
1093  int c = 0;
1094  for (size_t i = 0; i < fEnergyPeaks.size(); i++) {
1095  std::string fitName = (std::string) "g" + std::to_string(i);
1096  TF1* g = h->GetFunction(fitName.c_str());
1097  if (!g) {
1098  RESTWarning << "No fit ( " << fitName << " ) found for energy peak " << fEnergyPeaks[i]
1099  << " in histogram " << h->GetName() << p->RESTendl;
1100  continue;
1101  }
1102  gr->SetPoint(c++, g->GetParameter(1), fEnergyPeaks[i]);
1103  }
1104 
1105  // Add zero points if needed (if there are less than 2 points)
1106  while (gr->GetN() < 2) {
1107  gr->SetPoint(c++, 0, 0);
1108  }
1109 
1110  // Refit the calibration curve
1111  TF1* lf = nullptr;
1112  if (gr->GetFunction("linearFit"))
1113  lf = gr->GetFunction("linearFit");
1114  else
1115  lf = new TF1("linearFit", "pol1");
1116  gr->Fit(lf, "SQ"); // Q for quiet mode
1117 
1118  return std::make_pair(lf->GetParameter(0), lf->GetParameter(1));
1119 }
1120 
1127  auto [intercept, slope] = UpdateCalibrationFits(fFullSpectrum, fFullLinearFit);
1128  fFullSlope = slope;
1129  fFullIntercept = intercept;
1130 }
1131 
1147  if (module == nullptr) {
1148  RESTError << "TRestDataSetGainMap::Module::LoadConfigFromTiXmlElement: module is nullptr"
1149  << p->RESTendl;
1150  return;
1151  }
1152 
1153  std::string el = !module->Attribute("planeId") ? "Not defined" : module->Attribute("planeId");
1154  if (!(el.empty() || el == "Not defined")) this->SetPlaneId(StringToInteger(el));
1155  el = !module->Attribute("moduleId") ? "Not defined" : module->Attribute("moduleId");
1156  if (!(el.empty() || el == "Not defined")) this->SetModuleId(StringToInteger(el));
1157 
1158  el = !module->Attribute("moduleDefinitionCut") ? "Not defined" : module->Attribute("moduleDefinitionCut");
1159  if (!(el.empty() || el == "Not defined")) this->SetModuleDefinitionCut(el);
1160 
1161  el = !module->Attribute("numberOfSegmentsX") ? "Not defined" : module->Attribute("numberOfSegmentsX");
1162  if (!(el.empty() || el == "Not defined")) this->SetNumberOfSegmentsX(StringToInteger(el));
1163  el = !module->Attribute("numberOfSegmentsY") ? "Not defined" : module->Attribute("numberOfSegmentsY");
1164  if (!(el.empty() || el == "Not defined")) this->SetNumberOfSegmentsY(StringToInteger(el));
1165  el = !module->Attribute("readoutRange") ? "Not defined" : module->Attribute("readoutRange");
1166  if (!(el.empty() || el == "Not defined")) this->SetReadoutRange(StringTo2DVector(el));
1167 
1168  el = !module->Attribute("calibRange") ? "Not defined" : module->Attribute("calibRange");
1169  if (!(el.empty() || el == "Not defined")) this->SetCalibrationRange(StringTo2DVector(el));
1170  el = !module->Attribute("nBins") ? "Not defined" : module->Attribute("nBins");
1171  if (!(el.empty() || el == "Not defined")) this->SetNBins(StringToInteger(el));
1172 
1173  el = !module->Attribute("dataSetFileName") ? "Not defined" : module->Attribute("dataSetFileName");
1174  if (!(el.empty() || el == "Not defined")) this->SetDataSetFileName(el);
1175 
1176  el = !module->Attribute("zeroPoint") ? "Not defined" : module->Attribute("zeroPoint");
1177  if (!(el.empty() || el == "Not defined")) this->SetZeroPoint(ToLower(el) == "true");
1178  el = !module->Attribute("autoRangePeaks") ? "Not defined" : module->Attribute("autoRangePeaks");
1179  if (!(el.empty() || el == "Not defined")) this->SetAutoRangePeaks(ToLower(el) == "true");
1180 
1181  // Get peaks energy and range
1182  TiXmlElement* peakDefinition = (TiXmlElement*)module->FirstChildElement("peak");
1183  while (peakDefinition != nullptr) {
1184  double energy = 0;
1185  TVector2 range = TVector2(0, 0);
1186 
1187  std::string ell =
1188  !peakDefinition->Attribute("energy") ? "Not defined" : peakDefinition->Attribute("energy");
1189  if (ell.empty() || ell == "Not defined") {
1190  RESTError << "< peak variable key does not contain energy!" << p->RESTendl;
1191  exit(1);
1192  }
1193  energy = StringToDouble(ell);
1194 
1195  ell = !peakDefinition->Attribute("range") ? "Not defined" : peakDefinition->Attribute("range");
1196  if (!(ell.empty() || ell == "Not defined")) range = StringTo2DVector(ell);
1197 
1198  this->AddPeak(energy, range);
1199  peakDefinition = (TiXmlElement*)peakDefinition->NextSiblingElement();
1200  }
1201 }
1202 
1203 void TRestDataSetGainMap::Module::DrawSpectrum(const TVector2& position, bool drawFits, int color,
1204  TCanvas* c) {
1205  std::pair<size_t, size_t> index = GetIndexMatrix(position.X(), position.Y());
1206  DrawSpectrum(index.first, index.second, drawFits, color, c);
1207 }
1208 
1209 void TRestDataSetGainMap::Module::DrawSpectrum(const int index_x, const int index_y, bool drawFits, int color,
1210  TCanvas* c) {
1211  if (fSegSpectra.size() == 0) {
1212  RESTError << "Spectra matrix is empty." << p->RESTendl;
1213  return;
1214  }
1215  if (index_x < 0 || index_y < 0 || index_x >= (int)fSegSpectra.size() ||
1216  index_y >= (int)fSegSpectra.at(index_x).size()) {
1217  RESTError << "Index out of range." << p->RESTendl;
1218  return;
1219  }
1220  if (!fSegSpectra[index_x][index_y]) {
1221  RESTError << "No Spectrum for segment (" << index_x << ", " << index_y << ")." << p->RESTendl;
1222  return;
1223  }
1224 
1225  if (!c) {
1226  std::string t = "spectrum_" + std::to_string(fPlaneId) + "_" + std::to_string(fModuleId) + "_" +
1227  std::to_string(index_x) + "_" + std::to_string(index_y);
1228  c = new TCanvas(t.c_str(), t.c_str());
1229  }
1230 
1231  auto xLower = *std::next(fSplitX.begin(), index_x);
1232  auto xUpper = *std::next(fSplitX.begin(), index_x + 1);
1233  auto yLower = *std::next(fSplitY.begin(), index_y);
1234  auto yUpper = *std::next(fSplitY.begin(), index_y + 1);
1235  std::string tH = "Spectrum x=[" + DoubleToString(xLower, "%g") + ", " + DoubleToString(xUpper, "%g") +
1236  ") y=[" + DoubleToString(yLower, "%g") + ", " + DoubleToString(yUpper, "%g") + ");" +
1237  GetObservable() + ";counts";
1238  fSegSpectra[index_x][index_y]->SetTitle(tH.c_str());
1239 
1240  if (color > 0) fSegSpectra[index_x][index_y]->SetLineColor(color);
1241  size_t colorT = fSegSpectra[index_x][index_y]->GetLineColor();
1242  fSegSpectra[index_x][index_y]->Draw("same");
1243 
1244  if (drawFits)
1245  for (size_t c = 0; c < fEnergyPeaks.size(); c++) {
1246  auto fit = fSegSpectra[index_x][index_y]->GetFunction(("g" + std::to_string(c)).c_str());
1247  if (!fit)
1248  RESTWarning << "Fit for energy peak " << fEnergyPeaks[c] << " not found." << p->RESTendl;
1249  if (!fit) continue;
1250  fit->SetLineColor(c + 2 != colorT ? c + 2 : ++colorT); /* does not work with kRed, kBlue, etc.
1251  as they are not defined with the same number as the first 10 basic colors. See
1252  https://root.cern.ch/doc/master/classTColor.html#C01 and
1253  https://root.cern.ch/doc/master/classTColor.html#C02 */
1254  fit->Draw("same");
1255  }
1256 }
1257 
1277 void TRestDataSetGainMap::Module::DrawSpectrum(const bool drawFits, const int color, TCanvas* c) {
1278  if (fSegSpectra.size() == 0) {
1279  RESTError << "Spectra matrix is empty." << p->RESTendl;
1280  return;
1281  }
1282  if (!c) {
1283  std::string t = "spectrum_" + std::to_string(fPlaneId) + "_" + std::to_string(fModuleId);
1284  c = new TCanvas(t.c_str(), t.c_str());
1285  }
1286 
1287  size_t nPads = 0;
1288  for (const auto& object : *c->GetListOfPrimitives())
1289  if (object->InheritsFrom(TVirtualPad::Class())) ++nPads;
1290  if (nPads != 0 && nPads != fSegSpectra.size() * fSegSpectra.at(0).size()) {
1291  RESTError << "Canvas " << c->GetName() << " has " << nPads << " pads, but "
1292  << fSegSpectra.size() * fSegSpectra.at(0).size() << " are needed." << p->RESTendl;
1293  return;
1294  } else if (nPads == 0)
1295  c->Divide(fSegSpectra.size(), fSegSpectra.at(0).size());
1296 
1297  for (size_t i = 0; i < fSegSpectra.size(); i++) {
1298  for (size_t j = 0; j < fSegSpectra[i].size(); j++) {
1299  int pad = fSegSpectra.size() * (fSegSpectra[i].size() - 1) + 1 + i - fSegSpectra.size() * j;
1300  c->cd(pad);
1301  DrawSpectrum(i, j, drawFits, color, c);
1302  }
1303  }
1304 }
1305 void TRestDataSetGainMap::Module::DrawFullSpectrum(const bool drawFits, const int color, TCanvas* c) {
1306  if (!fFullSpectrum) {
1307  RESTError << "Spectrum is empty." << p->RESTendl;
1308  return;
1309  }
1310 
1311  if (!c) {
1312  std::string t = "fullSpc_" + std::to_string(fPlaneId) + "_" + std::to_string(fModuleId);
1313  c = new TCanvas(t.c_str(), t.c_str());
1314  }
1315  c->cd();
1316 
1317  fFullSpectrum->SetTitle(("Full spectrum;" + GetObservable() + ";counts").c_str());
1318 
1319  if (color > 0) fFullSpectrum->SetLineColor(color);
1320  size_t colorT = fFullSpectrum->GetLineColor();
1321  fFullSpectrum->Draw("same");
1322 
1323  if (drawFits)
1324  for (size_t c = 0; c < fEnergyPeaks.size(); c++) {
1325  auto fit = fFullSpectrum->GetFunction(("g" + std::to_string(c)).c_str());
1326  if (!fit) RESTWarning << "Fit for energy peak" << fEnergyPeaks[c] << " not found." << p->RESTendl;
1327  if (!fit) continue;
1328  fit->SetLineColor(c + 2 != colorT ? c + 2 : ++colorT); /* does not work with kRed, kBlue, etc.
1329  as they are not defined with the same number as the first 10 basic colors. See
1330  https://root.cern.ch/doc/master/classTColor.html#C01 and
1331  https://root.cern.ch/doc/master/classTColor.html#C02 */
1332  fit->Draw("same");
1333  }
1334 }
1335 
1336 void TRestDataSetGainMap::Module::DrawLinearFit(const TVector2& position, TCanvas* c) {
1337  std::pair<size_t, size_t> index = GetIndexMatrix(position.X(), position.Y());
1338  DrawLinearFit(index.first, index.second, c);
1339 }
1340 
1341 void TRestDataSetGainMap::Module::DrawLinearFit(const int index_x, const int index_y, TCanvas* c) {
1342  if (fSegLinearFit.size() == 0) {
1343  RESTError << "Spectra matrix is empty." << p->RESTendl;
1344  return;
1345  }
1346  if (index_x < 0 || index_y < 0 || index_x >= (int)fSegLinearFit.size() ||
1347  index_y >= (int)fSegLinearFit.at(index_x).size()) {
1348  RESTError << "Index out of range." << p->RESTendl;
1349  return;
1350  }
1351  if (!fSegLinearFit[index_x][index_y]) {
1352  RESTError << "No linear fit for segment (" << index_x << ", " << index_y << ")." << p->RESTendl;
1353  return;
1354  }
1355 
1356  if (!c) {
1357  std::string t = "linearFit_" + std::to_string(fPlaneId) + "_" + std::to_string(fModuleId) + "_" +
1358  std::to_string(index_x) + "_" + std::to_string(index_y);
1359  c = new TCanvas(t.c_str(), t.c_str());
1360  }
1361  auto xLower = *std::next(fSplitX.begin(), index_x);
1362  auto xUpper = *std::next(fSplitX.begin(), index_x + 1);
1363  auto yLower = *std::next(fSplitY.begin(), index_y);
1364  auto yUpper = *std::next(fSplitY.begin(), index_y + 1);
1365  std::string tH = "Linear Fit x=[" + DoubleToString(xLower, "%g") + ", " + DoubleToString(xUpper, "%g") +
1366  ") y=[" + DoubleToString(yLower, "%g") + ", " + DoubleToString(yUpper, "%g") + ");" +
1367  GetObservable() + ";energy";
1368  fSegLinearFit[index_x][index_y]->SetTitle(tH.c_str());
1369  fSegLinearFit[index_x][index_y]->Draw("AL*");
1370 }
1371 
1372 void TRestDataSetGainMap::Module::DrawLinearFit(TCanvas* c) {
1373  if (fSegLinearFit.size() == 0) {
1374  RESTError << "Spectra matrix is empty." << p->RESTendl;
1375  return;
1376  }
1377  if (!c) {
1378  std::string t = "linearFits_" + std::to_string(fPlaneId) + "_" + std::to_string(fModuleId);
1379  c = new TCanvas(t.c_str(), t.c_str());
1380  }
1381 
1382  size_t nPads = 0;
1383  for (const auto& object : *c->GetListOfPrimitives())
1384  if (object->InheritsFrom(TVirtualPad::Class())) ++nPads;
1385  if (nPads != 0 && nPads != fSegLinearFit.size() * fSegLinearFit.at(0).size()) {
1386  RESTError << "Canvas " << c->GetName() << " has " << nPads << " pads, but "
1387  << fSegLinearFit.size() * fSegLinearFit.at(0).size() << " are needed." << p->RESTendl;
1388  return;
1389  } else if (nPads == 0)
1390  c->Divide(fSegLinearFit.size(), fSegLinearFit.at(0).size());
1391 
1392  for (size_t i = 0; i < fSegLinearFit.size(); i++) {
1393  for (size_t j = 0; j < fSegLinearFit[i].size(); j++) {
1394  int pad = fSegLinearFit.size() * (fSegLinearFit[i].size() - 1) + 1 + i - fSegLinearFit.size() * j;
1395  c->cd(pad);
1396  DrawLinearFit(i, j, c);
1397  }
1398  }
1399 }
1400 
1409 void TRestDataSetGainMap::Module::DrawGainMap(const int peakNumber, bool fullModuleAsRef) {
1410  if (peakNumber < 0 || peakNumber >= (int)fEnergyPeaks.size()) {
1411  RESTError << "Peak number out of range (peakNumber should be between 0 and "
1412  << fEnergyPeaks.size() - 1 << " )" << p->RESTendl;
1413  return;
1414  }
1415  if (fSegLinearFit.size() == 0) {
1416  RESTError << "Linear fit matrix is empty." << p->RESTendl;
1417  return;
1418  }
1419  if (!fFullLinearFit) {
1420  RESTError << "Full linear fit is empty." << p->RESTendl;
1421  return;
1422  }
1423 
1424  double peakEnergy = fEnergyPeaks[peakNumber];
1425  std::string title = "Gain map for energy " + DoubleToString(peakEnergy, "%g") + ";" +
1426  GetSpatialObservableX() + ";" + GetSpatialObservableY(); // + " keV";
1427  std::string t = "gainMap" + std::to_string(peakNumber) + "_" + std::to_string(fPlaneId) + "_" +
1428  std::to_string(fModuleId);
1429  TCanvas* gainMap = new TCanvas(t.c_str(), t.c_str());
1430  gainMap->cd();
1431  TH2F* hGainMap = new TH2F(("h" + t).c_str(), title.c_str(), fNumberOfSegmentsX, fReadoutRange.X(),
1432  fReadoutRange.Y(), fNumberOfSegmentsY, fReadoutRange.X(), fReadoutRange.Y());
1433 
1434  double peakPosRef = fFullLinearFit->GetPointX(peakNumber);
1435  if (!fullModuleAsRef) {
1436  int index_x = fNumberOfSegmentsX > 0 ? (fNumberOfSegmentsX - 1) / 2 : 0;
1437  int index_y = fNumberOfSegmentsY > 0 ? (fNumberOfSegmentsY - 1) / 2 : 0;
1438  peakPosRef = fSegLinearFit[index_x][index_y]->GetPointX(peakNumber);
1439  }
1440 
1441  auto itX = fSplitX.begin();
1442  for (size_t i = 0; i < fSegLinearFit.size(); i++) {
1443  auto itY = fSplitY.begin();
1444  for (size_t j = 0; j < fSegLinearFit.at(0).size(); j++) {
1445  auto xLower = *itX;
1446  auto xUpper = *std::next(itX);
1447  auto yLower = *itY;
1448  auto yUpper = *std::next(itY);
1449  float xMean = (xUpper + xLower) / 2.;
1450  float yMean = (yUpper + yLower) / 2.;
1451  auto [index_x, index_y] = GetIndexMatrix(xMean, yMean);
1452  if (!fSegLinearFit[index_x][index_y]) continue;
1453  hGainMap->Fill(xMean, yMean, fSegLinearFit[index_x][index_y]->GetPointX(peakNumber) / peakPosRef);
1454  itY++;
1455  }
1456  itX++;
1457  }
1458  hGainMap->SetStats(0);
1459  hGainMap->Draw("colz");
1460  hGainMap->SetBarOffset(0.2);
1461  hGainMap->Draw("TEXT SAME");
1462 }
1463 
1469  RESTMetadata << "-----------------------------------------------" << p->RESTendl;
1470  RESTMetadata << " Plane ID: " << fPlaneId << p->RESTendl;
1471  RESTMetadata << " Module ID: " << fModuleId << p->RESTendl;
1472  RESTMetadata << " Definition cut: " << fDefinitionCut << p->RESTendl;
1473  RESTMetadata << p->RESTendl;
1474 
1475  RESTMetadata << " Calibration dataset: " << fDataSetFileName << p->RESTendl;
1476  RESTMetadata << p->RESTendl;
1477 
1478  RESTMetadata << " Energy peaks: ";
1479  for (const auto& peak : fEnergyPeaks) RESTMetadata << peak << ", ";
1480  RESTMetadata << p->RESTendl;
1481  bool anyRange = false;
1482  for (const auto& r : fRangePeaks)
1483  if (r.X() < r.Y()) {
1484  RESTMetadata << " Range peaks: ";
1485  anyRange = true;
1486  break;
1487  }
1488  if (anyRange)
1489  for (const auto& r : fRangePeaks) RESTMetadata << "(" << r.X() << ", " << r.Y() << ") ";
1490  if (anyRange) RESTMetadata << p->RESTendl;
1491  RESTMetadata << " Auto range peaks: " << (fAutoRangePeaks ? "true" : "false") << p->RESTendl;
1492  RESTMetadata << " Zero point: " << (fZeroPoint ? "true" : "false") << p->RESTendl;
1493  RESTMetadata << " Calibration range: (" << fCalibRange.X() << ", " << fCalibRange.Y() << " )"
1494  << p->RESTendl;
1495  RESTMetadata << " Number of bins: " << fNBins << p->RESTendl;
1496  RESTMetadata << p->RESTendl;
1497 
1498  RESTMetadata << " Number of segments X: " << fNumberOfSegmentsX << p->RESTendl;
1499  RESTMetadata << " Number of segments Y: " << fNumberOfSegmentsY << p->RESTendl;
1500  // RESTMetadata << " Draw: " << (fDrawVar ? "true" : "false") << p->RESTendl;
1501  RESTMetadata << " Readout range (" << fReadoutRange.X() << ", " << fReadoutRange.Y() << " )"
1502  << p->RESTendl;
1503  RESTMetadata << "SplitX: ";
1504  for (auto& i : fSplitX) {
1505  RESTMetadata << " " << i;
1506  }
1507  RESTMetadata << p->RESTendl;
1508  RESTMetadata << "SplitY: ";
1509  for (auto& i : fSplitY) {
1510  RESTMetadata << " " << i;
1511  }
1512  RESTMetadata << p->RESTendl;
1513  RESTMetadata << p->RESTendl;
1514 
1515  RESTMetadata << " Slope: " << p->RESTendl;
1516  size_t maxSize = 0;
1517  for (auto& x : fSlope)
1518  if (maxSize < x.size()) maxSize = x.size();
1519  for (size_t j = 0; j < maxSize; j++) {
1520  for (size_t k = 0; k < fSlope.size(); k++) {
1521  if (j < fSlope[k].size())
1522  RESTMetadata << DoubleToString(fSlope[k][fSlope[k].size() - 1 - j], "%.3e") << " ";
1523  else
1524  RESTMetadata << " ";
1525  }
1526  RESTMetadata << p->RESTendl;
1527  }
1528  RESTMetadata << " Intercept: " << p->RESTendl;
1529  maxSize = 0;
1530  for (auto& x : fIntercept)
1531  if (maxSize < x.size()) maxSize = x.size();
1532  for (size_t j = 0; j < maxSize; j++) {
1533  for (size_t k = 0; k < fIntercept.size(); k++) {
1534  if (j < fIntercept[k].size())
1535  RESTMetadata << DoubleToString(fIntercept[k][fIntercept[k].size() - 1 - j], "%+.3e") << " ";
1536  else
1537  RESTMetadata << " ";
1538  }
1539  RESTMetadata << p->RESTendl;
1540  }
1541  RESTMetadata << p->RESTendl;
1542  RESTMetadata << " Full slope: " << DoubleToString(fFullSlope, "%.3e") << p->RESTendl;
1543  RESTMetadata << " Full intercept: " << DoubleToString(fFullIntercept, "%+.3e") << p->RESTendl;
1544 
1545  RESTMetadata << "-----------------------------------------------" << p->RESTendl;
1546 }
A class to help on cuts definitions. To be used with TRestAnalysisTree.
Definition: TRestCut.h:31
void SetSplitY()
Function to set the class members for segmentation of the detector plane along the Y axis.
void SetSplits()
Function to set the class members for segmentation of the detector plane along the X and Y axis.
void SetSplitX()
Function to set the class members for segmentation of the detector plane along the X axis.
void DrawSpectrum(const bool drawFits=true, const int color=-1, TCanvas *c=nullptr)
Function to draw the spectrum for each segment of the module on the same canvas. The canvas is divide...
void LoadConfigFromTiXmlElement(const TiXmlElement *module)
Function to read the parameters from the RML element (TiXmlElement) and set those class members.
void GenerateGainMap()
Function that calculates the calibration parameters for each segment defined at fSplitX and fSplitY a...
double GetSlope(const double x, const double y) const
Function to get the calibration parameter slope for a given x and y position on the detector plane.
std::set< double > fSplitX
Split points in the x direction.
void Print() const
Prints on screen the information about the members of Module.
void Refit(const TVector2 &position, const double energy, const TVector2 &range)
Function to fit again manually a peak for a given segment of the module.
std::set< double > fSplitY
Split points in the y direction.
void RefitFullSpc(const double energy, const TVector2 &range)
Function to fit again manually a peak for the whole module spectrum. The calibration curve is updated...
const TRestDataSetGainMap * p
Pointer to the parent class.
void UpdateCalibrationFitsFullSpc()
Function to update the calibration curve for the whole module. The calibration curve is cleared and t...
void DrawGainMap(const int peakNumber=0, const bool fullModuleAsRef=true)
Function to draw the relative gain map for a given energy peak of the module.
double GetIntercept(const double x, const double y) const
Function to get the calibration parameter intercept for a given x and y position on the detector plan...
std::pair< int, int > GetIndexMatrix(const double x, const double y) const
Function to get the index of the matrix of calibration parameters for a given x and y position on the...
Metadata class to calculate,store and apply the gain corrected calibration of a group of detectors.
double GetInterceptParameter(const int planeID, const int moduleID, const double x, const double y)
Function to get the intercept parameter of the module with planeID and moduleID at physical position ...
double GetSlopeParameter(const int planeID, const int moduleID, const double x, const double y)
Function to get the slope parameter of the module with planeID and moduleID at physical position (x,...
std::string fObservable
Observable that will be used to calculate the gain map.
std::set< int > GetModuleIDs(const int planeId) const
Function to get a list (set) of the module IDs of the plane with planeID.
double GetInterceptParameterFullSpc(const int planeID, const int moduleID)
Function to get the intercept parameter of the whole module with planeID and moduleID.
void CalibrateDataSet(const std::string &dataSetFileName, std::string outputFileName="", std::vector< std::string > excludeColumns={})
Function to calibrate a dataset with this gain map.
~TRestDataSetGainMap()
Default destructor.
void GenerateGainMap()
Function to calculate the calibration parameters of all modules.
TRestDataSetGainMap()
Default constructor.
void Initialize() override
Making default settings.
std::vector< Module > fModulesCal
List of modules.
void SetModule(const Module &moduleCal)
Function to set a module calibration. If the module calibration already exists (same planeId and modu...
void InitFromConfigFile() override
Initialization of TRestDataSetGainMap members through a RML file.
double GetSlopeParameterFullSpc(const int planeID, const int moduleID)
Function to get the slope parameter of the whole module with planeID and moduleID.
void Export(const std::string &fileName="")
Function to export the calibration to the file fileName.
void PrintMetadata() override
Prints on screen the information about the metadata members.
std::map< int, std::set< int > > GetModuleIDs() const
Function to get the map of the module IDs for each plane ID.
std::string fSpatialObservableY
Observable that will be used to segmentize the gain map in the y direction.
std::string fCalibFileName
Name of the file that contains the calibration data.
TRestCut * fCut
Cut to be applied to the calibration data.
std::string fOutputFileName
Name of the file where the gain map was (or will be) exported.
std::set< int > GetPlaneIDs() const
Function to get a list (set) of the plane IDs.
std::string fSpatialObservableX
Observable that will be used to segmentize the gain map in the x direction.
Module * GetModule(const size_t index=0)
Function to retrieve the module calibration by index. Default is 0.
void Import(const std::string &fileName)
Function to import the calibration parameters from the root file fileName.
It allows to group a number of runs that satisfy given metadata conditions.
Definition: TRestDataSet.h:34
void Import(const std::string &fileName)
This function imports metadata from a root file it import metadata info from the previous dataSet whi...
ROOT::RDF::RNode GetDataFrame() const
Gives access to the RDataFrame.
Definition: TRestDataSet.h:129
ROOT::RDF::RNode MakeCut(const TRestCut *cut)
This function applies a TRestCut to the dataframe and returns a dataframe with the applied cuts....
void Export(const std::string &filename, std::vector< std::string > excludeColumns={})
It will generate an output file with the dataset compilation. Only the selected branches and the file...
A base class for any REST metadata class.
Definition: TRestMetadata.h:74
virtual void PrintMetadata()
Implemented it in the derived metadata class to print out specific metadata information.
endl_t RESTendl
Termination flag object for TRestStringOutput.
TiXmlElement * GetElement(std::string eleDeclare, TiXmlElement *e=nullptr)
Get an xml element from a given parent element, according to its declaration.
Int_t LoadConfigFromFile(const std::string &configFilename, const std::string &sectionName="")
Give the file name, find out the corresponding section. Then call the main starter.
TRestMetadata * InstantiateChildMetadata(int index, std::string pattern="")
This method will retrieve a new TRestMetadata instance of a child element of the present TRestMetadat...
virtual void InitFromConfigFile()
To make settings from rml file. This method must be implemented in the derived class.
TRestStringOutput::REST_Verbose_Level GetVerboseLevel()
returns the verboselevel in type of REST_Verbose_Level enumerator
void SetSectionName(std::string sName)
set the section name, clear the section content
std::string fConfigFileName
Full name of the rml file.
virtual Int_t Write(const char *name=nullptr, Int_t option=0, Int_t bufsize=0)
overwriting the write() method with fStore considered
TiXmlElement * GetNextElement(TiXmlElement *e)
Get the next sibling xml element of this element, with same eleDeclare.
@ REST_Info
+show most of the information for each steps
@ REST_Debug
+show the defined debug messages
static std::string GetFileNameExtension(const std::string &fullname)
Gets the file extension as the substring found after the latest ".".
Definition: TRestTools.cxx:823
static bool fileExists(const std::string &filename)
Returns true if the file (or directory) with path filename exists.
Definition: TRestTools.cxx:728
static bool isDataSet(const std::string &filename)
It checks if the file contains a dataset object.
Definition: TRestTools.cxx:754
static bool isRootFile(const std::string &filename)
Returns true if the filename has *.root* extension.
Definition: TRestTools.cxx:733
TClass * GetClassQuick(std::string type)
Bool_t MatchString(std::string str, std::string matcher)
This method matches a string with certain matcher. Returns true if matched. Supports wildcard charact...
Double_t StringToDouble(std::string in)
Gets a double from a string.
Int_t StringToInteger(std::string in)
Gets an integer from a string.
std::string DoubleToString(Double_t d, std::string format="%8.6e")
Gets a string from a double.
TVector2 StringTo2DVector(std::string in)
Gets a 2D-vector from a string.
std::string ToLower(std::string in)
Convert string to its lower case. Alternative of TString::ToLower.