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
140ClassImp(TRestDataSetGainMap);
145
160TRestDataSetGainMap::TRestDataSetGainMap(const char* configFilename, std::string name)
161 : TRestMetadata(configFilename) {
164}
165
170
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
222void 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
341TRestDataSetGainMap::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
354double 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
365double 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
376double 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
387double 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
397std::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
407std::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
417std::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
424TRestDataSetGainMap& 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
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
455void 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
493void 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
544std::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}
580double 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
602double 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
644void 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
677void 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
840std::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
949void 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
974void 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
1008void 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
1029void 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
1058void 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
1076std::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
1203void 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
1209void 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
1277void 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}
1305void 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
1336void 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
1341void 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
1372void 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
1409void 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.