REST-for-Physics  v2.3
Rare Event Searches ToolKit for Physics
Loading...
Searching...
No Matches
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
139
140#include "TRestDataSetGainMap.h"
141
142ClassImp(TRestDataSetGainMap);
147
162TRestDataSetGainMap::TRestDataSetGainMap(const char* configFilename, std::string name)
163 : TRestMetadata(configFilename) {
166}
167
172
179 this->Initialize();
181
182 // Load Module from RML
183 TiXmlElement* moduleDefinition = GetElement("module");
184 while (moduleDefinition != nullptr) {
185 fModulesCal.push_back(Module(*this));
186 fModulesCal.back().LoadConfigFromTiXmlElement(moduleDefinition);
187
188 moduleDefinition = GetNextElement(moduleDefinition);
189 }
190
191 fCut = (TRestCut*)InstantiateChildMetadata("TRestCut");
192
194}
195
200 for (auto& mod : fModulesCal) {
201 RESTInfo << "Generating gain map of plane " << mod.GetPlaneId() << " module " << mod.GetModuleId()
202 << RESTendl;
203 mod.GenerateGainMap();
205 mod.DrawSpectrum();
206 mod.DrawFullSpectrum();
207 mod.DrawGainMap();
208 }
209 }
210}
211
226void TRestDataSetGainMap::CalibrateDataSet(const std::string& dataSetFileName, std::string outputFileName,
227 std::vector<std::string> excludeColumns) {
228 if (fModulesCal.empty()) {
229 RESTError << "TRestDataSetGainMap::CalibrateDataSet: No modules defined." << RESTendl;
230 return;
231 }
232
233 TRestDataSet dataSet;
234 dataSet.EnableMultiThreading(true);
235
236 if (TRestTools::isDataSet(dataSetFileName)) {
237 dataSet.Import(dataSetFileName);
238 } else {
239 RESTWarning << dataSetFileName << " is not a dataset. Generating a temporal one..." << RESTendl;
240 // generate the dataset with the needed observables
241 dataSet.SetFilePattern(dataSetFileName);
242 dataSet.SetObservablesList({"*"}); // get all observables
243 dataSet.GenerateDataSet();
244 }
245
246 auto dataFrame = dataSet.GetDataFrame();
247
248 // Define a new column with the identifier (pmID) of the module for each row (event)
249 std::string pmIDname = (std::string)GetName() + "_pmID";
250 std::string modCut = fModulesCal[0].GetModuleDefinitionCut();
251 if (modCut.empty()) modCut = "1"; // if no cut is defined, use "1" (all events)
252 int pmID = fModulesCal[0].GetPlaneId() * 10 + fModulesCal[0].GetModuleId();
253
254 auto columnList = dataFrame.GetColumnNames();
255 if (std::find(columnList.begin(), columnList.end(), pmIDname) == columnList.end())
256 dataFrame = dataFrame.Define(pmIDname, modCut + " ? " + std::to_string(pmID) + " : -1");
257 else
258 dataFrame = dataFrame.Redefine(pmIDname, modCut + " ? " + std::to_string(pmID) + " : -1");
259
260 for (size_t n = 1; n < fModulesCal.size(); n++) {
261 modCut = fModulesCal[n].GetModuleDefinitionCut();
262 if (modCut.empty()) modCut = "1"; // if no cut is defined, use "1" (all events)
263 pmID = fModulesCal[n].GetPlaneId() * 10 + fModulesCal[n].GetModuleId();
264 dataFrame = dataFrame.Redefine(pmIDname, (modCut + " ? " + std::to_string(pmID) + " : " + pmIDname));
265 }
266
267 // Define a new column with the calibrated observable
268 auto calibrate = [this](double val, double x, double y, int pmID) {
269 for (auto& m : fModulesCal) {
270 if (pmID == m.GetPlaneId() * 10 + m.GetModuleId())
271 return m.GetSlope(x, y) * val + m.GetIntercept(x, y);
272 }
273 // RESTError << "TRestDataSetGainMap::CalibrateDataSet: Module not found for pmID " << pmID <<
274 // RESTendl;
275 return std::numeric_limits<double>::quiet_NaN();
276 };
277 std::string calibObsName = (std::string)GetName() + "_";
278 calibObsName += GetObservable().erase(0, GetObservable().find("_") + 1); // remove the "rawAna_" part
279 dataFrame = dataFrame.Define(calibObsName, calibrate,
281
282 // Define a new column with the calibrated observable for the whole module calibration
283 auto calibrateFullSpc = [this](double val, int pmID) {
284 for (auto& m : fModulesCal) {
285 if (pmID == m.GetPlaneId() * 10 + m.GetModuleId())
286 return m.GetSlopeFullSpc() * val + m.GetInterceptFullSpc();
287 }
288 // RESTError << "TRestDataSetGainMap::CalibrateDataSet: Module not found for pmID " << pmID <<
289 // RESTendl;
290 return std::numeric_limits<double>::quiet_NaN();
291 };
292 std::string calibObsNameFullSpc = (std::string)GetName() + "_";
293 calibObsNameFullSpc +=
294 GetObservable().erase(0, GetObservable().find("_") + 1); // remove the "rawAna_" part
295 calibObsNameFullSpc += "_NoSegmentation";
296 dataFrame = dataFrame.Define(calibObsNameFullSpc, calibrateFullSpc, {fObservable, pmIDname});
297
298 dataSet.SetDataFrame(dataFrame);
299
300 // Format the output file name and export the dataSet
301 if (outputFileName.empty()) outputFileName = dataSetFileName;
302 if (outputFileName == dataSetFileName) { // TRestDataSet cannot be overwritten
303 std::string gmName = GetName();
304 outputFileName = outputFileName.substr(0, outputFileName.find_last_of(".")); // remove extension
305 outputFileName += "_" + gmName + "." + TRestTools::GetFileNameExtension(dataSetFileName);
306 }
307
308 // Export dataset. Exclude columns if requested.
309 auto columns = dataSet.GetDataFrame().GetColumnNames();
310 std::set<std::string> excludeCol = TRestTools::GetMatchingStrings(columns, excludeColumns);
311 // Never exclude the calibObsName, calibObsNameFullSpc and pmIDname
312 excludeCol.erase(calibObsName);
313 excludeCol.erase(calibObsNameFullSpc);
314 excludeCol.erase(pmIDname);
315
316 RESTDebug << "Excluding columns: ";
317 for (auto& c : excludeCol) RESTDebug << c << ", ";
318 RESTDebug << RESTendl;
319
320 dataSet.Export(outputFileName, std::vector<std::string>(excludeCol.begin(), excludeCol.end()));
321
322 // Add this TRestDataSetGainMap metadata to the output file
323 TFile* f = TFile::Open(outputFileName.c_str(), "UPDATE");
324 this->Write();
325 f->Close();
326 delete f;
327}
328
334 if (index < fModulesCal.size()) return &fModulesCal[index];
335
336 RESTError << "No ModuleCalibration with index " << index;
337 if (fModulesCal.empty())
338 RESTError << ". There are no modules defined." << RESTendl;
339 else
340 RESTError << ". Max index is " << fModulesCal.size() - 1 << RESTendl;
341 return nullptr;
342}
343
348TRestDataSetGainMap::Module* TRestDataSetGainMap::GetModule(const int planeID, const int moduleID) {
349 for (auto& i : fModulesCal) {
350 if (i.GetPlaneId() == planeID && i.GetModuleId() == moduleID) return &i;
351 }
352 RESTError << "No ModuleCalibration with planeID " << planeID << " and moduleID " << moduleID << RESTendl;
353 return nullptr;
354}
355
361double TRestDataSetGainMap::GetSlopeParameter(const int planeID, const int moduleID, const double x,
362 const double y) {
363 Module* moduleCal = GetModule(planeID, moduleID);
364 if (moduleCal == nullptr) return 0; // return numeric_limits<double>::quiet_NaN()
365 return moduleCal->GetSlope(x, y);
366}
367
372double TRestDataSetGainMap::GetSlopeParameterFullSpc(const int planeID, const int moduleID) {
373 Module* moduleCal = GetModule(planeID, moduleID);
374 if (moduleCal == nullptr) return 0; // return numeric_limits<double>::quiet_NaN()
375 return moduleCal->GetSlopeFullSpc();
376}
377
383double TRestDataSetGainMap::GetInterceptParameter(const int planeID, const int moduleID, const double x,
384 const double y) {
385 Module* moduleCal = GetModule(planeID, moduleID);
386 if (moduleCal == nullptr) return 0; // return numeric_limits<double>::quiet_NaN()
387 return moduleCal->GetIntercept(x, y);
388}
389
394double TRestDataSetGainMap::GetInterceptParameterFullSpc(const int planeID, const int moduleID) {
395 Module* moduleCal = GetModule(planeID, moduleID);
396 if (moduleCal == nullptr) return 0; // return numeric_limits<double>::quiet_NaN()
397 return moduleCal->GetInterceptFullSpc();
398}
399
404std::set<int> TRestDataSetGainMap::GetPlaneIDs() const {
405 std::set<int> planeIDs;
406 for (const auto& mc : fModulesCal) planeIDs.insert(mc.GetPlaneId());
407 return planeIDs;
408}
409
414std::set<int> TRestDataSetGainMap::GetModuleIDs(const int planeId) const {
415 std::set<int> moduleIDs;
416 for (const auto& mc : fModulesCal)
417 if (mc.GetPlaneId() == planeId) moduleIDs.insert(mc.GetModuleId());
418 return moduleIDs;
419}
420
424std::map<int, std::set<int>> TRestDataSetGainMap::GetModuleIDs() const {
425 std::map<int, std::set<int>> moduleIds;
426 for (const int planeId : GetPlaneIDs())
427 moduleIds.insert(std::pair<int, std::set<int>>(planeId, GetModuleIDs(planeId)));
428 return moduleIds;
429}
430
431TRestDataSetGainMap& TRestDataSetGainMap::operator=(TRestDataSetGainMap& src) {
432 SetName(src.GetName());
433 fOutputFileName = src.GetOutputFileName();
434 fObservable = src.GetObservable();
435 fSpatialObservableX = src.GetSpatialObservableX();
436 fSpatialObservableY = src.GetSpatialObservableY();
437 fSpatialObservableXSecondary = src.GetSpatialObservableXSecondary();
438 fSpatialObservableYSecondary = src.GetSpatialObservableYSecondary();
439 fCut = src.GetCut();
440 fModulesCal.clear();
441 for (auto pID : src.GetPlaneIDs())
442 for (auto mID : src.GetModuleIDs(pID)) fModulesCal.push_back(*src.GetModule(pID, mID));
443 return *this;
444}
445
451 for (auto& i : fModulesCal) {
452 if (i.GetPlaneId() == moduleCal.GetPlaneId() && i.GetModuleId() == moduleCal.GetModuleId()) {
453 i = moduleCal;
454 return;
455 }
456 }
457 fModulesCal.push_back(moduleCal);
458}
459
464void TRestDataSetGainMap::Import(const std::string& fileName) {
465 if (fileName.empty()) {
466 RESTError << "No input calibration file defined" << RESTendl;
467 return;
468 }
469
470 if (TRestTools::isRootFile(fileName)) {
471 RESTInfo << "Opening " << fileName << RESTendl;
472 TFile* f = TFile::Open(fileName.c_str(), "READ");
473 if (f == nullptr) {
474 RESTError << "Cannot open calibration file " << fileName << RESTendl;
475 return;
476 }
477
478 TRestDataSetGainMap* cal = nullptr;
479 if (f != nullptr) {
480 TIter nextkey(f->GetListOfKeys());
481 TKey* key;
482 while ((key = (TKey*)nextkey())) {
483 std::string kName = key->GetClassName();
484 if (REST_Reflection::GetClassQuick(kName.c_str()) != nullptr &&
485 REST_Reflection::GetClassQuick(kName.c_str())->InheritsFrom("TRestDataSetGainMap")) {
486 cal = f->Get<TRestDataSetGainMap>(key->GetName());
487 *this = *cal;
488 }
489 }
490 }
491 } else
492 RESTError << "File extension not supported for " << fileName << RESTendl;
494}
495
502void TRestDataSetGainMap::Export(const std::string& fileName) {
503 if (!fileName.empty()) fOutputFileName = fileName;
504 if (fOutputFileName.empty()) {
505 RESTError << "No output file defined" << RESTendl;
506 return;
507 }
508
510 TFile* f = TFile::Open(fOutputFileName.c_str(), "UPDATE");
511 this->Write(GetName());
512 f->Close();
513 delete f;
514 RESTInfo << "Calibration saved to " << fOutputFileName << RESTendl;
515 } else
516 RESTError << "File extension for " << fOutputFileName << "is not supported." << RESTendl;
517}
518
524 RESTMetadata << " Calibration dataset: " << fCalibFileName << RESTendl;
525 if (fCut) {
526 RESTMetadata << " Cuts applied: ";
527 /* print only cutStrings and paramCut because
528 TRestDataSet::MakeCut() uses fCut->GetCutStrings() and fCut->GetParamCut() */
529 for (const auto& cut : fCut->GetCutStrings()) RESTMetadata << cut << ", " << RESTendl;
530 for (const auto& cut : fCut->GetParamCut())
531 RESTMetadata << cut.first << " " << cut.second << ", " << RESTendl;
532 }
533 RESTMetadata << " Output file: " << fOutputFileName << RESTendl;
534 RESTMetadata << RESTendl;
535 RESTMetadata << " Number of planes: " << GetNumberOfPlanes() << RESTendl;
536 RESTMetadata << " Number of modules: " << GetNumberOfModules() << RESTendl;
537 RESTMetadata << " Calibration observable: " << fObservable << RESTendl;
538 RESTMetadata << " Spatial observable X: " << fSpatialObservableX << RESTendl;
539 RESTMetadata << " Spatial observable Y: " << fSpatialObservableY << RESTendl;
540 if (!fSpatialObservableXSecondary.empty())
541 RESTMetadata << " Secondary spatial observable X: " << fSpatialObservableXSecondary << RESTendl;
542 if (!fSpatialObservableYSecondary.empty())
543 RESTMetadata << " Secondary spatial observable Y: " << fSpatialObservableYSecondary << RESTendl;
544 RESTMetadata << "-----------------------------------------------" << RESTendl;
545 for (auto& i : fModulesCal) i.Print();
546 RESTMetadata << "***********************************************" << RESTendl;
547 RESTMetadata << RESTendl;
548}
549
557std::pair<int, int> TRestDataSetGainMap::Module::GetIndexMatrix(const double x, const double y) const {
558 int index_x = -1, index_y = -1;
559
560 if (fSplitX.upper_bound(x) != fSplitX.end()) {
561 index_x = std::distance(fSplitX.begin(), fSplitX.upper_bound(x)) - 1;
562 if (index_x < 0) {
563 RESTWarning << "index_x < 0 for x = " << x << " and fSplitX[0]=" << *fSplitX.begin()
564 << p->RESTendl;
565 index_x = 0;
566 }
567 } else {
568 RESTWarning << "x is out of split for x = " << x << p->RESTendl;
569 index_x = fSplitX.size() - 2;
570 }
571
572 if (fSplitY.upper_bound(y) != fSplitY.end()) {
573 index_y = std::distance(fSplitY.begin(), fSplitY.upper_bound(y)) - 1;
574 if (index_y < 0) {
575 RESTWarning << "index_y < 0 for y = " << y << " and fSplitY[0]=" << *fSplitY.begin()
576 << p->RESTendl;
577 index_y = 0;
578 }
579 } else {
580 RESTWarning << "y is out of split for y = " << y << p->RESTendl;
581 index_y = fSplitY.size() - 2;
582 }
583
584 return std::make_pair(index_x, index_y);
585}
593double TRestDataSetGainMap::Module::GetSlope(const double x, const double y) const {
594 auto [index_x, index_y] = GetIndexMatrix(x, y);
595 if (fSlope.empty()) {
596 RESTError << "Calibration slope matrix is empty. Returning 0" << p->RESTendl;
597 return 0;
598 }
599
600 if (index_x > (int)fSlope.size() || index_y > (int)fSlope.at(0).size()) {
601 RESTError << "Index out of range. Returning 0" << p->RESTendl;
602 return 0;
603 }
604
605 return fSlope[index_x][index_y];
606}
607
615double TRestDataSetGainMap::Module::GetIntercept(const double x, const double y) const {
616 auto [index_x, index_y] = GetIndexMatrix(x, y);
617 if (fIntercept.empty()) {
618 RESTError << "Calibration constant matrix is empty. Returning 0" << p->RESTendl;
619 return 0;
620 }
621
622 if (index_x > (int)fIntercept.size() || index_y > (int)fIntercept.at(0).size()) {
623 RESTError << "Index out of range. Returning 0" << p->RESTendl;
624 return 0;
625 }
626
627 return fIntercept[index_x][index_y];
628}
629
634 SetSplitX();
635 SetSplitY();
636}
644 if (fNumberOfSegmentsX < 1) {
645 RESTError << "SetSplitX: fNumberOfSegmentsX must be >=1." << p->RESTendl;
646 return;
647 }
648 std::set<double> split;
649 for (int i = 0; i <= fNumberOfSegmentsX; i++) { // <= so that the last segment is included
650 double x =
651 fReadoutRange.X() + ((fReadoutRange.Y() - fReadoutRange.X()) / (float)fNumberOfSegmentsX) * i;
652 split.insert(x);
653 }
654 SetSplitX(split);
655}
656
657void TRestDataSetGainMap::Module::SetSplitX(const std::set<double>& splitX) {
658 if (splitX.size() < 2) {
659 RESTError << "SetSplitX: split size must be >=2 (start and end of range must be included)."
660 << p->RESTendl;
661 return;
662 }
663 if (!fSlope.empty())
664 RESTWarning << "SetSplitX: changing split but current gain map and calibration paremeters correspond "
665 "to previous splitting. Use GenerateGainMap() to update them."
666 << p->RESTendl;
667 fSplitX = splitX;
668 fNumberOfSegmentsX = fSplitX.size() - 1;
669}
677 if (fNumberOfSegmentsY < 1) {
678 RESTError << "SetSplitY: fNumberOfSegmentsY must be >=1." << p->RESTendl;
679 return;
680 }
681 std::set<double> split;
682 for (int i = 0; i <= fNumberOfSegmentsY; i++) { // <= so that the last segment is included
683 double y =
684 fReadoutRange.X() + ((fReadoutRange.Y() - fReadoutRange.X()) / (float)fNumberOfSegmentsY) * i;
685 split.insert(y);
686 }
687 SetSplitY(split);
688}
689
690void TRestDataSetGainMap::Module::SetSplitY(const std::set<double>& splitY) {
691 if (splitY.size() < 2) {
692 RESTError << "SetSplitY: split size must be >=2 (start and end of range must be included)."
693 << p->RESTendl;
694 return;
695 }
696 if (!fSlope.empty())
697 RESTWarning << "SetSplitY: changing split but current gain map and calibration paremeters correspond "
698 "to previous splitting. Use GenerateGainMap() to update them."
699 << p->RESTendl;
700 fSplitY = splitY;
701 fNumberOfSegmentsY = fSplitY.size() - 1;
702}
703
721 //--- Initial checks and settings ---
722 std::string dsFileName = fDataSetFileName;
723 if (dsFileName.empty()) dsFileName = p->GetCalibrationFileName();
724 if (dsFileName.empty()) {
725 RESTError << "No calibration file defined" << p->RESTendl;
726 return;
727 }
728
729 TRestDataSet dataSet;
730 dataSet.EnableMultiThreading(true);
731 if (TRestTools::isDataSet(dsFileName)) {
732 dataSet.Import(dsFileName);
733 fDataSetFileName = dsFileName;
734 } else {
735 RESTWarning << dsFileName << " is not a dataset. Generating a temporal one..." << p->RESTendl;
736 // get all the observables needed for the gain map
737 std::vector<std::string> obsList;
738
739 obsList.push_back(p->GetObservable());
740 obsList.push_back(p->GetSpatialObservableX());
741 obsList.push_back(p->GetSpatialObservableY());
742 if (!p->GetSpatialObservableXSecondary().empty())
743 obsList.push_back(p->GetSpatialObservableXSecondary());
744 if (!p->GetSpatialObservableYSecondary().empty())
745 obsList.push_back(p->GetSpatialObservableYSecondary());
746
747 // look for observables (characterized by having a _ in the name) in the definition cut
748 auto modDefCutObs = TRestTools::GetObservablesInString(fDefinitionCut, true);
749 obsList.insert(obsList.end(), modDefCutObs.begin(), modDefCutObs.end());
750
751 // look for observables in the cut
752 for (const auto& cut : p->GetCut()->GetCutStrings()) {
753 auto cutObs = TRestTools::GetObservablesInString(cut, true);
754 obsList.insert(obsList.end(), cutObs.begin(), cutObs.end());
755 }
756 for (const auto& [variable, condition] : p->GetCut()->GetParamCut()) {
757 auto cutObs = TRestTools::GetObservablesInString(variable, true);
758 obsList.insert(obsList.end(), cutObs.begin(), cutObs.end());
759 // not sure if any obs can be in the condition. Just in case...
760 cutObs = TRestTools::GetObservablesInString(condition, true);
761 obsList.insert(obsList.end(), cutObs.begin(), cutObs.end());
762 }
763
764 // remove duplicates
765 std::sort(obsList.begin(), obsList.end());
766 obsList.erase(std::unique(obsList.begin(), obsList.end()), obsList.end());
767
768 // generate the dataset with the needed observables
769 dataSet.SetFilePattern(dsFileName);
770 dataSet.SetObservablesList(obsList);
771 dataSet.GenerateDataSet();
772 fDataSetFileName = dsFileName;
773 }
774
775 dataSet.SetDataFrame(dataSet.MakeCut(p->GetCut()));
776
777 if (fSplitX.empty()) SetSplitX();
778 if (fSplitY.empty()) SetSplitY();
779
780 //--- Get the calibration range if not provided (default is 0,0) ---
781 if (fCalibRange.X() >= fCalibRange.Y()) {
782 // Get spectrum for this file
783 std::string cut = fDefinitionCut;
784 if (cut.empty()) cut = "1";
785 auto histo = dataSet.GetDataFrame().Filter(cut).Histo1D({"temp", "", fNBins, 0, 0}, GetObservable());
786 std::unique_ptr<TH1F> hpunt = std::unique_ptr<TH1F>(static_cast<TH1F*>(histo->Clone()));
787 // double xMin = hpunt->GetXaxis()->GetXmin();
788 double xMax = hpunt->GetXaxis()->GetXmax();
789
790 // Reduce the range to avoid the possible empty (nCounts<1%) end part of the spectrum
791 double fraction = 1, nAtEndSpc = 0, step = 0.66;
792 while (nAtEndSpc * 1. / hpunt->Integral() < 0.01 && fraction > 0.001) {
793 fraction *= step;
794 nAtEndSpc = hpunt->Integral(hpunt->FindFixBin(hpunt->GetXaxis()->GetXmax() * fraction),
795 hpunt->FindFixBin(hpunt->GetXaxis()->GetXmax()));
796 }
797 xMax = hpunt->GetXaxis()->GetXmax() * fraction /
798 step; // previous step is the last one that nAtEndSpc<1%
799 // Set the calibration range if needed
800 // fCalibRange.SetX(xMin);
801 fCalibRange.SetY(xMax);
802 hpunt.reset(); // delete hpunt;
803 RESTDebug << "Calibration range (auto)set to (" << fCalibRange.X() << "," << fCalibRange.Y() << ")"
804 << p->RESTendl;
805 }
806
807 // --- Definition of histogram whole module ---
808 std::string hModuleName = "hSpc_" + std::to_string(fPlaneId) + "_" + std::to_string(fModuleId);
809 delete fFullSpectrum;
810 fFullSpectrum = new TH1F(hModuleName.c_str(), "", fNBins, fCalibRange.X(), fCalibRange.Y());
811
812 // build the spectrum for the whole module
813 std::string cut = fDefinitionCut;
814 if (cut.empty()) cut = "1";
815 auto histoMod = dataSet.GetDataFrame().Filter(cut).Histo1D(
816 {"tempMod", "", fNBins, fCalibRange.X(), fCalibRange.Y()}, GetObservable());
817 std::unique_ptr<TH1F> hpuntMod = std::unique_ptr<TH1F>(static_cast<TH1F*>(histoMod->Clone()));
818 fFullSpectrum->Add(hpuntMod.get());
819
820 //--- Definition of histogram matrix ---
821 std::vector<std::vector<TH1F*>> h(fNumberOfSegmentsX, std::vector<TH1F*>(fNumberOfSegmentsY, nullptr));
822 for (size_t i = 0; i < h.size(); i++) {
823 for (size_t j = 0; j < h.at(0).size(); j++) {
824 std::string name = hModuleName + "_" + std::to_string(i) + "_" + std::to_string(j);
825 h[i][j] = new TH1F(name.c_str(), "", fNBins, fCalibRange.X(),
826 fCalibRange.Y()); // h[column][row] equivalent to h[x][y]
827 }
828 }
829
830 // build the spectrum for each segment
831 auto itX = fSplitX.begin();
832 for (size_t i = 0; i < h.size(); i++) {
833 auto itY = fSplitY.begin();
834 for (size_t j = 0; j < h.at(0).size(); j++) {
835 // Get the segment limits from the splits
836 auto xLower = *itX;
837 auto xUpper = *std::next(itX);
838 auto yLower = *itY;
839 auto yUpper = *std::next(itY);
840
841 std::string segment_cut = "";
842 if (!GetSpatialObservableX().empty())
843 segment_cut += GetSpatialObservableX() + ">=" + std::to_string(xLower) + "&&" +
844 GetSpatialObservableX() + "<" + std::to_string(xUpper);
845 if (!GetSpatialObservableY().empty())
846 segment_cut += "&&" + GetSpatialObservableY() + ">=" + std::to_string(yLower) + "&&" +
847 GetSpatialObservableY() + "<" + std::to_string(yUpper);
848 if (!GetSpatialObservableXSecondary().empty())
849 segment_cut += "&&" + GetSpatialObservableXSecondary() + ">=" + std::to_string(xLower) +
850 "&&" + GetSpatialObservableXSecondary() + "<" + std::to_string(xUpper);
851 if (!GetSpatialObservableYSecondary().empty())
852 segment_cut += "&&" + GetSpatialObservableYSecondary() + ">=" + std::to_string(yLower) +
853 "&&" + GetSpatialObservableYSecondary() + "<" + std::to_string(yUpper);
854 if (!fDefinitionCut.empty()) segment_cut += "&&" + fDefinitionCut;
855 if (segment_cut.empty()) segment_cut = "1";
856 RESTExtreme << "Segment[" << i << "][" << j << "] cut: " << segment_cut << p->RESTendl;
857 auto histo = dataSet.GetDataFrame()
858 .Filter(segment_cut)
859 .Histo1D({"temp", "", h[i][j]->GetNbinsX(), h[i][j]->GetXaxis()->GetXmin(),
860 h[i][j]->GetXaxis()->GetXmax()},
861 GetObservable());
862 std::unique_ptr<TH1F> hpunt = std::unique_ptr<TH1F>(static_cast<TH1F*>(histo->Clone()));
863 h[i][j]->Add(hpunt.get());
864 hpunt.reset(); // delete hpunt;
865 itY++;
866 }
867 itX++;
868 }
869
870 //--- Fit every peak energy for every segment ---
871 std::vector<std::vector<double>> calParSlope(fNumberOfSegmentsX,
872 std::vector<double>(fNumberOfSegmentsY, 0));
873 std::vector<std::vector<double>> calParIntercept(fNumberOfSegmentsX,
874 std::vector<double>(fNumberOfSegmentsY, 0));
875 fSegLinearFit = std::vector(h.size(), std::vector<TGraph*>(h.at(0).size(), nullptr));
876 for (size_t i = 0; i < h.size(); i++)
877 for (int j = 0; j < (int)h.at(0).size(); j++) {
878 fSegLinearFit[i][j] = new TGraph();
879 auto [intercept, slope] = FitPeaks(h[i][j], fSegLinearFit[i][j]);
880 calParSlope[i][j] = slope;
881 calParIntercept[i][j] = intercept;
882 }
883 fSlope = calParSlope;
884 fIntercept = calParIntercept;
885 fSegSpectra = h;
886
887 //--- Fit every peak energy for the whole module ---
888 delete fFullLinearFit;
889 fFullLinearFit = new TGraph();
890 auto [intercept, slope] = FitPeaks(fFullSpectrum, fFullLinearFit);
891 fFullSlope = slope;
892 fFullIntercept = intercept;
893}
894
895std::pair<double, double> TRestDataSetGainMap::Module::FitPeaks(TH1F* hSeg, TGraph* gr) {
896 if (!hSeg) {
897 RESTError << "No histogram for fitting" << p->RESTendl;
898 return std::make_pair(0, 0);
899 }
900 if (hSeg->Integral() == 0) {
901 RESTError << "Empty spectrum " << hSeg->GetName() << p->RESTendl;
902 return std::make_pair(0, 0);
903 }
904 std::shared_ptr<TGraph> graph = std::shared_ptr<TGraph>(new TGraph());
905 RESTExtreme << "Fitting peaks for " << hSeg->GetName() << p->RESTendl;
906
907 // Search for peaks --> peakPos
908 std::unique_ptr<TSpectrum> s(new TSpectrum(2 * fEnergyPeaks.size() + 1));
909 std::vector<double> peakPos;
910 s->Search(hSeg, 2, "goff", 0.1);
911 for (int k = 0; k < s->GetNPeaks(); k++) peakPos.push_back(s->GetPositionX()[k]);
912 std::sort(peakPos.begin(), peakPos.end(), std::greater<double>());
913 const double ratio =
914 peakPos.size() == 0 ? 1 : peakPos.front() / fEnergyPeaks.front(); // to estimate peak position
915
916 // Initialize graph for linear fit
917 graph->SetName("grFit");
918 graph->SetTitle((";" + GetObservable() + ";energy").c_str());
919
920 // Fit every energy peak
921 int c = 0;
922 double mu = 0;
923 for (const auto& energy : fEnergyPeaks) {
924 RESTExtreme << "\t fitting energy " << DoubleToString(energy, "%g") << p->RESTendl;
925 // estimation of the peak position is between start and end
926 double pos = energy * ratio;
927 double start = pos * 0.8;
928 double end = pos * 1.2;
929 if (fRangePeaks.at(c).X() < fRangePeaks.at(c).Y()) { // if range is defined use it
930 start = fRangePeaks.at(c).X();
931 end = fRangePeaks.at(c).Y();
932 }
933
934 do {
935 if (fAutoRangePeaks) {
936 if (peakPos.size() > 0) {
937 // Find the peak position that is between start and end
938 pos = peakPos.at(0);
939 while (!(start < pos && pos < end)) {
940 // if none of the peak position is
941 // between start and end, use the greater one.
942 if (pos == peakPos.back()) {
943 pos = peakPos.at(0);
944 break;
945 }
946 pos = *std::next(std::find(peakPos.begin(), peakPos.end(),
947 pos)); // get next peak position
948 }
949 peakPos.erase(std::find(peakPos.begin(), peakPos.end(),
950 pos)); // remove this peak position from the list
951 // final estimation of the peak range (idem fitting window) with this peak
952 // position pos
953 start = pos * 0.8;
954 end = pos * 1.2;
955 const double relDist = peakPos.size() > 0 ? (pos - peakPos.front()) / pos : 999;
956 if (relDist < 0.2) { // if the next peak is too close reduce the window width
957 start = pos * (1 - relDist / 2);
958 end = pos * (1 + relDist / 2);
959 }
960 }
961 }
962
963 std::string name = "g" + std::to_string(c);
964 TF1* g = new TF1(name.c_str(), "gaus", start, end);
965 RESTExtreme << "\t\tat " << DoubleToString(pos, "%.3g") << ". Range("
966 << DoubleToString(start, "%.3g") << ", " << DoubleToString(end, "%.3g") << ")"
967 << p->RESTendl;
968
969 if (hSeg->GetFunction(name.c_str())) // remove previous fit
970 hSeg->GetListOfFunctions()->Remove(hSeg->GetFunction(name.c_str()));
971
972 hSeg->Fit(g, "R+Q0"); // use 0 to not draw the fit but save it
973 mu = g->GetParameter(1);
974 RESTExtreme << "\t\tgaus mean " << DoubleToString(mu, "%g") << p->RESTendl;
975 } while (fAutoRangePeaks && peakPos.size() > 0 &&
976 !(start < mu && mu < end)); // avoid small peaks on main peak tail
977 graph->SetPoint(c++, mu, energy);
978 }
979 s.reset(); // delete s;
980
981 if (fZeroPoint) graph->SetPoint(c++, 0, 0);
982 while (graph->GetN() < 2) { // minimun 2 points needed for linear fit
983 graph->SetPoint(c++, 0, 0);
984 SetZeroPoint(true);
985 RESTDebug << "Not enough points for linear fit. Adding and setting zero point to true" << p->RESTendl;
986 }
987
988 // Linear fit
989 std::unique_ptr<TF1> linearFit;
990 linearFit = std::unique_ptr<TF1>(new TF1("linearFit", "pol1"));
991 graph->Fit("linearFit", "SQ"); // Q for quiet mode
992
993 if (gr) *gr = *(TGraph*)graph->Clone(); // if nullptr is passed, do not copy the graph
994 return std::make_pair(linearFit->GetParameter(0), linearFit->GetParameter(1));
995}
996
1004void TRestDataSetGainMap::Module::Refit(const TVector2& position, const double energyPeak,
1005 const TVector2& range) {
1006 auto [index_x, index_y] = GetIndexMatrix(position.X(), position.Y());
1007 int peakNumber = -1;
1008 for (size_t i = 0; i < fEnergyPeaks.size(); i++)
1009 if (fEnergyPeaks.at(i) == energyPeak) {
1010 peakNumber = i;
1011 break;
1012 }
1013 if (peakNumber == -1) {
1014 RESTError << "Energy " << energyPeak << " not found in the list of energy peaks" << p->RESTendl;
1015 return;
1016 }
1017 Refit((size_t)index_x, (size_t)index_y, (size_t)peakNumber, range);
1018}
1019
1029void TRestDataSetGainMap::Module::Refit(const size_t x, const size_t y, const size_t peakNumber,
1030 const TVector2& range) {
1031 if (fSegSpectra.empty()) {
1032 RESTError << "No gain map found. Use GenerateGainMap() first." << p->RESTendl;
1033 return;
1034 }
1035 if (x >= fSegSpectra.size() || y >= fSegSpectra.at(0).size()) {
1036 RESTError << "Segment with index (" << x << ", " << y << ") not found" << p->RESTendl;
1037 return;
1038 }
1039 if (peakNumber >= fEnergyPeaks.size()) {
1040 RESTError << "Peak with index " << peakNumber << " not found" << p->RESTendl;
1041 return;
1042 }
1043
1044 // Refit the desired peak
1045 std::string name = "g" + std::to_string(peakNumber);
1046 TF1* g = new TF1(name.c_str(), "gaus", range.X(), range.Y());
1047 TH1F* h = fSegSpectra.at(x).at(y);
1048 while (h->GetFunction(name.c_str())) // clear previous fits for this peakNumber
1049 h->GetListOfFunctions()->Remove(h->GetFunction(name.c_str()));
1050 h->Fit(g, "R+Q0"); // use 0 to not draw the fit but save it
1051
1052 // Change the point of the graph
1053 UpdateCalibrationFits(x, y);
1054}
1055
1063void TRestDataSetGainMap::Module::RefitFullSpc(const double energyPeak, const TVector2& range) {
1064 int peakNumber = -1;
1065 for (size_t i = 0; i < fEnergyPeaks.size(); i++)
1066 if (fEnergyPeaks.at(i) == energyPeak) {
1067 peakNumber = i;
1068 break;
1069 }
1070 if (peakNumber == -1) {
1071 RESTError << "Energy " << energyPeak << " not found in the list of energy peaks" << p->RESTendl;
1072 return;
1073 }
1074 RefitFullSpc((size_t)peakNumber, range);
1075}
1076
1084void TRestDataSetGainMap::Module::RefitFullSpc(const size_t peakNumber, const TVector2& range) {
1085 if (!fFullSpectrum) {
1086 RESTError << "No gain map found. Use GenerateGainMap() first." << p->RESTendl;
1087 return;
1088 }
1089 if (peakNumber >= fEnergyPeaks.size()) {
1090 RESTError << "Peak with index " << peakNumber << " not found" << p->RESTendl;
1091 return;
1092 }
1093
1094 // Refit the desired peak
1095 std::string name = "g" + std::to_string(peakNumber);
1096 TF1* g = new TF1(name.c_str(), "gaus", range.X(), range.Y());
1097 while (fFullSpectrum->GetFunction(name.c_str())) // clear previous fits for this peakNumber
1098 fFullSpectrum->GetListOfFunctions()->Remove(fFullSpectrum->GetFunction(name.c_str()));
1099 fFullSpectrum->Fit(g, "R+Q0"); // use 0 to not draw the fit but save it
1100
1101 // Change the point of the graph
1102 UpdateCalibrationFitsFullSpc();
1103}
1104
1113void TRestDataSetGainMap::Module::UpdateCalibrationFits(const size_t x, const size_t y) {
1114 if (fSegSpectra.empty()) {
1115 RESTError << "No gain map found. Use GenerateGainMap() first." << p->RESTendl;
1116 return;
1117 }
1118 if (x >= fSegSpectra.size() || y >= fSegSpectra.at(0).size()) {
1119 RESTError << "Segment with index (" << x << ", " << y << ") not found" << p->RESTendl;
1120 return;
1121 }
1122
1123 TH1F* h = fSegSpectra.at(x).at(y);
1124 TGraph* gr = fSegLinearFit.at(x).at(y);
1125
1126 auto [intercept, slope] = UpdateCalibrationFits(h, gr);
1127 fSlope[x][y] = slope;
1128 fIntercept[x][y] = intercept;
1129}
1130
1131std::pair<double, double> TRestDataSetGainMap::Module::UpdateCalibrationFits(TH1F* h, TGraph* gr) {
1132 if (!h) {
1133 RESTError << "No histogram for updating fits" << p->RESTendl;
1134 return std::make_pair(0, 0);
1135 }
1136 if (!gr) {
1137 RESTError << "No graph for updating fits" << p->RESTendl;
1138 return std::make_pair(0, 0);
1139 }
1140 if (h->Integral() == 0) {
1141 RESTError << "Empty spectrum " << h->GetName() << p->RESTendl;
1142 return std::make_pair(0, 0);
1143 }
1144
1145 // Clear the points of the graph
1146 for (size_t i = 0; i < fEnergyPeaks.size(); i++) gr->RemovePoint(i);
1147 // Add the new points to the graph
1148 int c = 0;
1149 for (size_t i = 0; i < fEnergyPeaks.size(); i++) {
1150 std::string fitName = (std::string) "g" + std::to_string(i);
1151 TF1* g = h->GetFunction(fitName.c_str());
1152 if (!g) {
1153 RESTWarning << "No fit ( " << fitName << " ) found for energy peak " << fEnergyPeaks[i]
1154 << " in histogram " << h->GetName() << p->RESTendl;
1155 continue;
1156 }
1157 gr->SetPoint(c++, g->GetParameter(1), fEnergyPeaks[i]);
1158 }
1159
1160 // Add zero points if needed (if there are less than 2 points)
1161 while (gr->GetN() < 2) {
1162 gr->SetPoint(c++, 0, 0);
1163 }
1164
1165 // Refit the calibration curve
1166 TF1* lf = nullptr;
1167 if (gr->GetFunction("linearFit"))
1168 lf = gr->GetFunction("linearFit");
1169 else
1170 lf = new TF1("linearFit", "pol1");
1171 gr->Fit(lf, "SQ"); // Q for quiet mode
1172
1173 return std::make_pair(lf->GetParameter(0), lf->GetParameter(1));
1174}
1175
1182 auto [intercept, slope] = UpdateCalibrationFits(fFullSpectrum, fFullLinearFit);
1183 fFullSlope = slope;
1184 fFullIntercept = intercept;
1185}
1186
1202 if (module == nullptr) {
1203 RESTError << "TRestDataSetGainMap::Module::LoadConfigFromTiXmlElement: module is nullptr"
1204 << p->RESTendl;
1205 return;
1206 }
1207
1208 std::string el = !module->Attribute("planeId") ? "Not defined" : module->Attribute("planeId");
1209 if (!(el.empty() || el == "Not defined")) this->SetPlaneId(StringToInteger(el));
1210 el = !module->Attribute("moduleId") ? "Not defined" : module->Attribute("moduleId");
1211 if (!(el.empty() || el == "Not defined")) this->SetModuleId(StringToInteger(el));
1212
1213 el = !module->Attribute("moduleDefinitionCut") ? "Not defined" : module->Attribute("moduleDefinitionCut");
1214 if (!(el.empty() || el == "Not defined")) this->SetModuleDefinitionCut(el);
1215
1216 el = !module->Attribute("numberOfSegmentsX") ? "Not defined" : module->Attribute("numberOfSegmentsX");
1217 if (!(el.empty() || el == "Not defined")) this->SetNumberOfSegmentsX(StringToInteger(el));
1218 el = !module->Attribute("numberOfSegmentsY") ? "Not defined" : module->Attribute("numberOfSegmentsY");
1219 if (!(el.empty() || el == "Not defined")) this->SetNumberOfSegmentsY(StringToInteger(el));
1220 el = !module->Attribute("readoutRange") ? "Not defined" : module->Attribute("readoutRange");
1221 if (!(el.empty() || el == "Not defined")) this->SetReadoutRange(StringTo2DVector(el));
1222
1223 el = !module->Attribute("calibRange") ? "Not defined" : module->Attribute("calibRange");
1224 if (!(el.empty() || el == "Not defined")) this->SetCalibrationRange(StringTo2DVector(el));
1225 el = !module->Attribute("nBins") ? "Not defined" : module->Attribute("nBins");
1226 if (!(el.empty() || el == "Not defined")) this->SetNBins(StringToInteger(el));
1227
1228 el = !module->Attribute("dataSetFileName") ? "Not defined" : module->Attribute("dataSetFileName");
1229 if (!(el.empty() || el == "Not defined")) this->SetDataSetFileName(el);
1230
1231 el = !module->Attribute("zeroPoint") ? "Not defined" : module->Attribute("zeroPoint");
1232 if (!(el.empty() || el == "Not defined")) this->SetZeroPoint(ToLower(el) == "true");
1233 el = !module->Attribute("autoRangePeaks") ? "Not defined" : module->Attribute("autoRangePeaks");
1234 if (!(el.empty() || el == "Not defined")) this->SetAutoRangePeaks(ToLower(el) == "true");
1235
1236 // Get peaks energy and range
1237 TiXmlElement* peakDefinition = (TiXmlElement*)module->FirstChildElement("peak");
1238 while (peakDefinition != nullptr) {
1239 double energy = 0;
1240 TVector2 range = TVector2(0, 0);
1241
1242 std::string ell =
1243 !peakDefinition->Attribute("energy") ? "Not defined" : peakDefinition->Attribute("energy");
1244 if (ell.empty() || ell == "Not defined") {
1245 RESTError << "< peak variable key does not contain energy!" << p->RESTendl;
1246 exit(1);
1247 }
1248 energy = StringToDouble(ell);
1249
1250 ell = !peakDefinition->Attribute("range") ? "Not defined" : peakDefinition->Attribute("range");
1251 if (!(ell.empty() || ell == "Not defined")) range = StringTo2DVector(ell);
1252
1253 this->AddPeak(energy, range);
1254 peakDefinition = (TiXmlElement*)peakDefinition->NextSiblingElement();
1255 }
1256}
1257
1258void TRestDataSetGainMap::Module::DrawSpectrum(const TVector2& position, bool drawFits, int color,
1259 TCanvas* c) {
1260 std::pair<size_t, size_t> index = GetIndexMatrix(position.X(), position.Y());
1261 DrawSpectrum(index.first, index.second, drawFits, color, c);
1262}
1263
1264void TRestDataSetGainMap::Module::DrawSpectrum(const int index_x, const int index_y, bool drawFits, int color,
1265 TCanvas* c) {
1266 if (fSegSpectra.size() == 0) {
1267 RESTError << "Spectra matrix is empty." << p->RESTendl;
1268 return;
1269 }
1270 if (index_x < 0 || index_y < 0 || index_x >= (int)fSegSpectra.size() ||
1271 index_y >= (int)fSegSpectra.at(index_x).size()) {
1272 RESTError << "Index out of range." << p->RESTendl;
1273 return;
1274 }
1275 if (!fSegSpectra[index_x][index_y]) {
1276 RESTError << "No Spectrum for segment (" << index_x << ", " << index_y << ")." << p->RESTendl;
1277 return;
1278 }
1279
1280 if (!c) {
1281 std::string t = "spectrum_" + std::to_string(fPlaneId) + "_" + std::to_string(fModuleId) + "_" +
1282 std::to_string(index_x) + "_" + std::to_string(index_y);
1283 c = new TCanvas(t.c_str(), t.c_str());
1284 }
1285
1286 auto xLower = *std::next(fSplitX.begin(), index_x);
1287 auto xUpper = *std::next(fSplitX.begin(), index_x + 1);
1288 auto yLower = *std::next(fSplitY.begin(), index_y);
1289 auto yUpper = *std::next(fSplitY.begin(), index_y + 1);
1290 std::string tH = "Spectrum x=[" + DoubleToString(xLower, "%g") + ", " + DoubleToString(xUpper, "%g") +
1291 ") y=[" + DoubleToString(yLower, "%g") + ", " + DoubleToString(yUpper, "%g") + ");" +
1292 GetObservable() + ";counts";
1293 fSegSpectra[index_x][index_y]->SetTitle(tH.c_str());
1294
1295 if (color > 0) fSegSpectra[index_x][index_y]->SetLineColor(color);
1296 size_t colorT = fSegSpectra[index_x][index_y]->GetLineColor();
1297 fSegSpectra[index_x][index_y]->Draw("same");
1298
1299 if (drawFits)
1300 for (size_t c = 0; c < fEnergyPeaks.size(); c++) {
1301 auto fit = fSegSpectra[index_x][index_y]->GetFunction(("g" + std::to_string(c)).c_str());
1302 if (!fit)
1303 RESTWarning << "Fit for energy peak " << fEnergyPeaks[c] << " not found." << p->RESTendl;
1304 if (!fit) continue;
1305 fit->SetLineColor(c + 2 != colorT ? c + 2 : ++colorT); /* does not work with kRed, kBlue, etc.
1306 as they are not defined with the same number as the first 10 basic colors. See
1307 https://root.cern.ch/doc/master/classTColor.html#C01 and
1308 https://root.cern.ch/doc/master/classTColor.html#C02 */
1309 fit->Draw("same");
1310 }
1311}
1312
1332void TRestDataSetGainMap::Module::DrawSpectrum(const bool drawFits, const int color, TCanvas* c) {
1333 if (fSegSpectra.size() == 0) {
1334 RESTError << "Spectra matrix is empty." << p->RESTendl;
1335 return;
1336 }
1337 if (!c) {
1338 std::string t = "spectrum_" + std::to_string(fPlaneId) + "_" + std::to_string(fModuleId);
1339 c = new TCanvas(t.c_str(), t.c_str());
1340 }
1341
1342 size_t nPads = 0;
1343 for (const auto& object : *c->GetListOfPrimitives())
1344 if (object->InheritsFrom(TVirtualPad::Class())) ++nPads;
1345 if (nPads != 0 && nPads != fSegSpectra.size() * fSegSpectra.at(0).size()) {
1346 RESTError << "Canvas " << c->GetName() << " has " << nPads << " pads, but "
1347 << fSegSpectra.size() * fSegSpectra.at(0).size() << " are needed." << p->RESTendl;
1348 return;
1349 } else if (nPads == 0)
1350 c->Divide(fSegSpectra.size(), fSegSpectra.at(0).size());
1351
1352 for (size_t i = 0; i < fSegSpectra.size(); i++) {
1353 for (size_t j = 0; j < fSegSpectra[i].size(); j++) {
1354 int pad = fSegSpectra.size() * (fSegSpectra[i].size() - 1) + 1 + i - fSegSpectra.size() * j;
1355 c->cd(pad);
1356 DrawSpectrum(i, j, drawFits, color, c);
1357 }
1358 }
1359}
1360void TRestDataSetGainMap::Module::DrawFullSpectrum(const bool drawFits, const int color, TCanvas* c) {
1361 if (!fFullSpectrum) {
1362 RESTError << "Spectrum is empty." << p->RESTendl;
1363 return;
1364 }
1365
1366 if (!c) {
1367 std::string t = "fullSpc_" + std::to_string(fPlaneId) + "_" + std::to_string(fModuleId);
1368 c = new TCanvas(t.c_str(), t.c_str());
1369 }
1370 c->cd();
1371
1372 fFullSpectrum->SetTitle(("Full spectrum;" + GetObservable() + ";counts").c_str());
1373
1374 if (color > 0) fFullSpectrum->SetLineColor(color);
1375 size_t colorT = fFullSpectrum->GetLineColor();
1376 fFullSpectrum->Draw("same");
1377
1378 if (drawFits)
1379 for (size_t c = 0; c < fEnergyPeaks.size(); c++) {
1380 auto fit = fFullSpectrum->GetFunction(("g" + std::to_string(c)).c_str());
1381 if (!fit) RESTWarning << "Fit for energy peak" << fEnergyPeaks[c] << " not found." << p->RESTendl;
1382 if (!fit) continue;
1383 fit->SetLineColor(c + 2 != colorT ? c + 2 : ++colorT); /* does not work with kRed, kBlue, etc.
1384 as they are not defined with the same number as the first 10 basic colors. See
1385 https://root.cern.ch/doc/master/classTColor.html#C01 and
1386 https://root.cern.ch/doc/master/classTColor.html#C02 */
1387 fit->Draw("same");
1388 }
1389}
1390
1391void TRestDataSetGainMap::Module::DrawLinearFit(const TVector2& position, TCanvas* c) {
1392 std::pair<size_t, size_t> index = GetIndexMatrix(position.X(), position.Y());
1393 DrawLinearFit(index.first, index.second, c);
1394}
1395
1396void TRestDataSetGainMap::Module::DrawLinearFit(const int index_x, const int index_y, TCanvas* c) {
1397 if (fSegLinearFit.size() == 0) {
1398 RESTError << "Spectra matrix is empty." << p->RESTendl;
1399 return;
1400 }
1401 if (index_x < 0 || index_y < 0 || index_x >= (int)fSegLinearFit.size() ||
1402 index_y >= (int)fSegLinearFit.at(index_x).size()) {
1403 RESTError << "Index out of range." << p->RESTendl;
1404 return;
1405 }
1406 if (!fSegLinearFit[index_x][index_y]) {
1407 RESTError << "No linear fit for segment (" << index_x << ", " << index_y << ")." << p->RESTendl;
1408 return;
1409 }
1410
1411 if (!c) {
1412 std::string t = "linearFit_" + std::to_string(fPlaneId) + "_" + std::to_string(fModuleId) + "_" +
1413 std::to_string(index_x) + "_" + std::to_string(index_y);
1414 c = new TCanvas(t.c_str(), t.c_str());
1415 }
1416 auto xLower = *std::next(fSplitX.begin(), index_x);
1417 auto xUpper = *std::next(fSplitX.begin(), index_x + 1);
1418 auto yLower = *std::next(fSplitY.begin(), index_y);
1419 auto yUpper = *std::next(fSplitY.begin(), index_y + 1);
1420 std::string tH = "Linear Fit x=[" + DoubleToString(xLower, "%g") + ", " + DoubleToString(xUpper, "%g") +
1421 ") y=[" + DoubleToString(yLower, "%g") + ", " + DoubleToString(yUpper, "%g") + ");" +
1422 GetObservable() + ";energy";
1423 fSegLinearFit[index_x][index_y]->SetTitle(tH.c_str());
1424 fSegLinearFit[index_x][index_y]->Draw("AL*");
1425}
1426
1427void TRestDataSetGainMap::Module::DrawLinearFit(TCanvas* c) {
1428 if (fSegLinearFit.size() == 0) {
1429 RESTError << "Spectra matrix is empty." << p->RESTendl;
1430 return;
1431 }
1432 if (!c) {
1433 std::string t = "linearFits_" + std::to_string(fPlaneId) + "_" + std::to_string(fModuleId);
1434 c = new TCanvas(t.c_str(), t.c_str());
1435 }
1436
1437 size_t nPads = 0;
1438 for (const auto& object : *c->GetListOfPrimitives())
1439 if (object->InheritsFrom(TVirtualPad::Class())) ++nPads;
1440 if (nPads != 0 && nPads != fSegLinearFit.size() * fSegLinearFit.at(0).size()) {
1441 RESTError << "Canvas " << c->GetName() << " has " << nPads << " pads, but "
1442 << fSegLinearFit.size() * fSegLinearFit.at(0).size() << " are needed." << p->RESTendl;
1443 return;
1444 } else if (nPads == 0)
1445 c->Divide(fSegLinearFit.size(), fSegLinearFit.at(0).size());
1446
1447 for (size_t i = 0; i < fSegLinearFit.size(); i++) {
1448 for (size_t j = 0; j < fSegLinearFit[i].size(); j++) {
1449 int pad = fSegLinearFit.size() * (fSegLinearFit[i].size() - 1) + 1 + i - fSegLinearFit.size() * j;
1450 c->cd(pad);
1451 DrawLinearFit(i, j, c);
1452 }
1453 }
1454}
1455
1465void TRestDataSetGainMap::Module::DrawGainMap(const int peakNumber, const bool fullModuleAsRef,
1466 const bool showText) {
1467 if (peakNumber < 0 || peakNumber >= (int)fEnergyPeaks.size()) {
1468 RESTError << "Peak number out of range (peakNumber should be between 0 and "
1469 << fEnergyPeaks.size() - 1 << " )" << p->RESTendl;
1470 return;
1471 }
1472 if (fSegLinearFit.size() == 0) {
1473 RESTError << "Linear fit matrix is empty." << p->RESTendl;
1474 return;
1475 }
1476 if (!fFullLinearFit) {
1477 RESTError << "Full linear fit is empty." << p->RESTendl;
1478 return;
1479 }
1480
1481 double peakEnergy = fEnergyPeaks[peakNumber];
1482 std::string title = "Gain map for energy " + DoubleToString(peakEnergy, "%g") + ";" +
1483 GetSpatialObservableX() + ";" + GetSpatialObservableY(); // + " keV";
1484 std::string t = "gainMap" + std::to_string(peakNumber) + "_" + std::to_string(fPlaneId) + "_" +
1485 std::to_string(fModuleId);
1486 TCanvas* gainMap = new TCanvas(t.c_str(), t.c_str());
1487 gainMap->cd();
1488 TH2F* hGainMap = new TH2F(("h" + t).c_str(), title.c_str(), fNumberOfSegmentsX, fReadoutRange.X(),
1489 fReadoutRange.Y(), fNumberOfSegmentsY, fReadoutRange.X(), fReadoutRange.Y());
1490
1491 double peakPosRef = fFullLinearFit->GetPointX(peakNumber);
1492 if (!fullModuleAsRef) {
1493 int index_x = fNumberOfSegmentsX > 0 ? (fNumberOfSegmentsX - 1) / 2 : 0;
1494 int index_y = fNumberOfSegmentsY > 0 ? (fNumberOfSegmentsY - 1) / 2 : 0;
1495 peakPosRef = fSegLinearFit[index_x][index_y]->GetPointX(peakNumber);
1496 }
1497
1498 auto itX = fSplitX.begin();
1499 for (size_t i = 0; i < fSegLinearFit.size(); i++) {
1500 auto itY = fSplitY.begin();
1501 for (size_t j = 0; j < fSegLinearFit.at(0).size(); j++) {
1502 auto xLower = *itX;
1503 auto xUpper = *std::next(itX);
1504 auto yLower = *itY;
1505 auto yUpper = *std::next(itY);
1506 float xMean = (xUpper + xLower) / 2.;
1507 float yMean = (yUpper + yLower) / 2.;
1508 auto [index_x, index_y] = GetIndexMatrix(xMean, yMean);
1509 if (!fSegLinearFit[index_x][index_y]) continue;
1510 hGainMap->Fill(xMean, yMean, fSegLinearFit[index_x][index_y]->GetPointX(peakNumber) / peakPosRef);
1511 itY++;
1512 }
1513 itX++;
1514 }
1515 hGainMap->SetStats(0);
1516 hGainMap->Draw("colz");
1517 hGainMap->SetBarOffset(0.2);
1518 if (showText) {
1519 hGainMap->Draw("TEXT SAME");
1520 }
1521}
1522
1528 RESTMetadata << "-----------------------------------------------" << p->RESTendl;
1529 RESTMetadata << " Plane ID: " << fPlaneId << p->RESTendl;
1530 RESTMetadata << " Module ID: " << fModuleId << p->RESTendl;
1531 RESTMetadata << " Definition cut: " << fDefinitionCut << p->RESTendl;
1532 RESTMetadata << p->RESTendl;
1533
1534 RESTMetadata << " Calibration dataset: " << fDataSetFileName << p->RESTendl;
1535 RESTMetadata << p->RESTendl;
1536
1537 RESTMetadata << " Energy peaks: ";
1538 for (const auto& peak : fEnergyPeaks) RESTMetadata << peak << ", ";
1539 RESTMetadata << p->RESTendl;
1540 bool anyRange = false;
1541 for (const auto& r : fRangePeaks)
1542 if (r.X() < r.Y()) {
1543 RESTMetadata << " Range peaks: ";
1544 anyRange = true;
1545 break;
1546 }
1547 if (anyRange)
1548 for (const auto& r : fRangePeaks) RESTMetadata << "(" << r.X() << ", " << r.Y() << ") ";
1549 if (anyRange) RESTMetadata << p->RESTendl;
1550 RESTMetadata << " Auto range peaks: " << (fAutoRangePeaks ? "true" : "false") << p->RESTendl;
1551 RESTMetadata << " Zero point: " << (fZeroPoint ? "true" : "false") << p->RESTendl;
1552 RESTMetadata << " Calibration range: (" << fCalibRange.X() << ", " << fCalibRange.Y() << " )"
1553 << p->RESTendl;
1554 RESTMetadata << " Number of bins: " << fNBins << p->RESTendl;
1555 RESTMetadata << p->RESTendl;
1556
1557 RESTMetadata << " Number of segments X: " << fNumberOfSegmentsX << p->RESTendl;
1558 RESTMetadata << " Number of segments Y: " << fNumberOfSegmentsY << p->RESTendl;
1559 // RESTMetadata << " Draw: " << (fDrawVar ? "true" : "false") << p->RESTendl;
1560 RESTMetadata << " Readout range (" << fReadoutRange.X() << ", " << fReadoutRange.Y() << " )"
1561 << p->RESTendl;
1562 RESTMetadata << "SplitX: ";
1563 for (auto& i : fSplitX) {
1564 RESTMetadata << " " << i;
1565 }
1566 RESTMetadata << p->RESTendl;
1567 RESTMetadata << "SplitY: ";
1568 for (auto& i : fSplitY) {
1569 RESTMetadata << " " << i;
1570 }
1571 RESTMetadata << p->RESTendl;
1572 RESTMetadata << p->RESTendl;
1573
1574 RESTMetadata << " Slope: " << p->RESTendl;
1575 size_t maxSize = 0;
1576 for (auto& x : fSlope)
1577 if (maxSize < x.size()) maxSize = x.size();
1578 for (size_t j = 0; j < maxSize; j++) {
1579 for (size_t k = 0; k < fSlope.size(); k++) {
1580 if (j < fSlope[k].size())
1581 RESTMetadata << DoubleToString(fSlope[k][fSlope[k].size() - 1 - j], "%.3e") << " ";
1582 else
1583 RESTMetadata << " ";
1584 }
1585 RESTMetadata << p->RESTendl;
1586 }
1587 RESTMetadata << " Intercept: " << p->RESTendl;
1588 maxSize = 0;
1589 for (auto& x : fIntercept)
1590 if (maxSize < x.size()) maxSize = x.size();
1591 for (size_t j = 0; j < maxSize; j++) {
1592 for (size_t k = 0; k < fIntercept.size(); k++) {
1593 if (j < fIntercept[k].size())
1594 RESTMetadata << DoubleToString(fIntercept[k][fIntercept[k].size() - 1 - j], "%+.3e") << " ";
1595 else
1596 RESTMetadata << " ";
1597 }
1598 RESTMetadata << p->RESTendl;
1599 }
1600 RESTMetadata << p->RESTendl;
1601 RESTMetadata << " Full slope: " << DoubleToString(fFullSlope, "%.3e") << p->RESTendl;
1602 RESTMetadata << " Full intercept: " << DoubleToString(fFullIntercept, "%+.3e") << p->RESTendl;
1603
1604 RESTMetadata << "-----------------------------------------------" << p->RESTendl;
1605}
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...
void DrawGainMap(const int peakNumber=0, const bool fullModuleAsRef=true, const bool showText=true)
Function to draw the relative gain map for a given energy peak of the module.
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...
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.
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.
std::string fSpatialObservableXSecondary
Secondary observable that will be used to segmentize the gain map in the x direction (if needed)
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::string fSpatialObservableYSecondary
Secondary observable that will be used to segmentize the gain map in the y direction (if needed)
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.
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.
ROOT::RDF::RNode MakeCut(const TRestCut *cut)
This function applies a TRestCut to the dataframe and returns a dataframe with the applied cuts....
void GenerateDataSet()
This function generates the data frame with the filelist and column names (or observables) that have ...
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.
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 ".".
static std::set< std::string > GetMatchingStrings(const std::vector< std::string > &stack, const std::vector< std::string > &wantedStrings)
Returns a set of strings that match the wanted strings from the stack. The wanted strings can contain...
static bool isDataSet(const std::string &filename)
It checks if the file contains a dataset object.
static std::vector< std::string > GetObservablesInString(const std::string &observablesStr, bool removeDuplicates=true)
Returns a vector with the observables names found in the input string. The observables names must con...
static bool isRootFile(const std::string &filename)
Returns true if the filename has *.root* extension.
TClass * GetClassQuick()
Get the type of a "class" object, returning the wrapped type identifier "TClass".
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.