-
Theodore Efremov authored
[AlPhaPha] Updated readme and added a finetuning CutAuto that change behaviour at the edges also added way to rename and add tcut easily by hand
Theodore Efremov authored[AlPhaPha] Updated readme and added a finetuning CutAuto that change behaviour at the edges also added way to rename and add tcut easily by hand
CutAutoDEE_FineTuning.C 25.27 KiB
#include "CutAutoDEE_FineTuning.h"
#include <numeric>
#include <set>
#include <iostream>
#include <unordered_map>
// Important variable
#define STEP 2; // Step to parse through the histogram in X
#define BINSIZE 10; // Size of the bin in X on which to projectY the plot
#define THRESHOLDY 75.0;
#define THRESHOLDX 200.0;
//Main
void CutAutoDEE_FineTuning(){
// Retrieve initial histogram
TFile *inFile = new TFile("Output/DE_E_merged.root","read");
TH2F *hDE_E = (TH2F*)inFile->Get("DE_E");
// Inverse data to use tspectrum
for (int xBin=1; xBin<= hDE_E->GetNbinsX();xBin++){
for (int yBin=1; yBin<= hDE_E->GetNbinsY();yBin++){
hDE_E->SetBinContent(xBin,yBin , -hDE_E->GetBinContent(xBin,yBin) );
}
}
//Store result of peak finding
//This is not a good way to do this : We should normalize the structure of the code by
//directly using a PeakData class to store each of the peak.
vector<int> *vPeaks = new vector<int>() ;
vector<TSpectrum*> *vPeakFinder = new vector<TSpectrum*>();
vector<vector<Double_t>> *LinePos = new vector<vector<Double_t>> ;
// Tspectrum used to find the peak : If you have problem with peak finding
// adjust the setting in this function
// Step and Binsize are used in this function
Double_t sigma = 3;
Double_t threshold = 0.0001;
PeakFinder(vPeakFinder,vPeaks,hDE_E,LinePos,sigma,threshold);
// Link the peak, this is where the most critical variable are.
// Specifically the maximum distance between each peak and the minimum number of peak in a line
vector<TCutG*> CutLine = PeakLinker(LinePos);
// Inverse data for plotting
for (int xBin=1; xBin<= hDE_E->GetNbinsX();xBin++){
for (int yBin=1; yBin<= hDE_E->GetNbinsY();yBin++){
hDE_E->SetBinContent(xBin,yBin , -hDE_E->GetBinContent(xBin,yBin));
}
}
//Plot
gStyle->SetPalette(kRainBow);
TCanvas* c2 = new TCanvas("c2","c2");
hDE_E->Draw("colz");
TCanvas* c3 = new TCanvas("c3","c3");
hDE_E->Draw("colz");
for (auto cut : CutLine){
cut->Draw("SAME");
}
//Save the cut !
TFile *ofile = new TFile("Output/CutAutoZtest.root","RECREATE");
for (auto cut : CutLine){
cut->Write();
}
ofile->Close();
}
/**
* Find the peak of an histogram by subdivinding the histogram on it's x axis.
* Store the number of peak and their position in different vector
*
* input and Output:
* vector<TSpectrum*> *sPeak : Vector of TSpectrum to output
* vector<int> *vPeak : Vector of number of peak for each bin range
* vector<vector<Double_t>> *LinePos : Vector of position of each peak for each bin range
*/
void PeakFinder(vector<TSpectrum*> *sPeak , vector<int> *vPeak, TH2F *h, vector<vector<Double_t>> *LinePos, Double_t sigma, Double_t threshold){
// Initialising
int nbinsX = h->GetNbinsX();
int iter = 0;
int stepParsing = STEP;
int SizeBin = BINSIZE;
// Looping on the histogram x axis.
for (int i = 1; i <= nbinsX; i += stepParsing) {
// Limit in binnumber
iter +=1;
int x1 = i;
int x2 = std::min(i + SizeBin - 1, nbinsX);
// Projection on Y between the binnumber
TString projName = Form("projY_%d_to_%d", x1, x2);
TH1D* projY = h->ProjectionY(projName, x1, x2);
projY->SetTitle(Form("Projection Y: bins %d to %d", x1, x2));
// PeakFinder
TSpectrum *PeakFinder = new TSpectrum(600);
int nPeaks = PeakFinder->Search(projY,sigma,"",threshold);
//Store npeaks and tspectrum
vPeak->push_back(nPeaks); // number of peak for this bin range
sPeak->push_back(PeakFinder);
//Save the position of each peak
Double_t * x = PeakFinder->GetPositionX();
for(int peaks = 0 ; peaks < nPeaks ; peaks++){
vector<Double_t> Position(2);
Position.at(0) = (double(i) + double(SizeBin)/2.0) * h->GetXaxis()->GetBinWidth(4) ; //Retrieve E position as the center
//of the bin
Position.at(1) = PeakFinder->GetPositionX()[peaks]; // Position in DE
LinePos->push_back(Position);
}
//Test
if (true && iter ==50){
// Draw the histogram
TCanvas* c = new TCanvas(Form("c%d",i), Form("Histogram with E = %f",(h->GetXaxis()->GetBinCenter(i))), 800, 600);
projY->Draw();
}
}
}
/**
* Link the different point to create TCUT from the peak found previously
* To do so a max range is given and then all point separated by a distance inferior to it are linked.
*
* Input :
* vec<vec<Double>> *LinePos : pointer to a vector of length number of peak holding position in x and y for each peak
* Output:
* TCUTG : a cut for each charge in vamos
*/
vector<TCutG*> PeakLinker(vector<vector<Double_t>> *LinePos){
//Retrieve position of peaks
vector<Double_t> X , Y ;
for (int ipeak = 0; ipeak<LinePos->size() ; ipeak ++){
X.push_back(LinePos->at(ipeak).at(0));
Y.push_back(LinePos->at(ipeak).at(1));
}
//===========================================================================================================
// 1. Find the threshold between two point
//===========================================================================================================
// Find the distance between two point in X.
//Sort the vec
vector<Double_t> XSub(X);
sort(XSub.begin(),XSub.end());
//Take only the unique element
set<Double_t> SetX(XSub.begin() , XSub.end());
vector<Double_t> XThresh ;
for (Double_t elem : SetX) {
XThresh.push_back(elem);
}
//Find the average dist in X
vector<Double_t> DiffX = diff(XThresh);
Double_t ThresholdXestim ;
Double_t ThresholdX ;
for (int i =0 ; i<DiffX.size() ; i++) { ThresholdXestim += DiffX.at(i);}
ThresholdXestim = ThresholdXestim / DiffX.size();
// In Y the threshold is not easily findable, However it's critical that the threshold
// is superior to the average distance in X. If not line can't be found by construction
// The threshold should also be inferior to the distance between each line in Y
// To do so adjust the bin STEP at the beginning of the code.
//Very important set
ThresholdX = THRESHOLDX;
Double_t ThresholdY = THRESHOLDY;
cout << "Threshold X : " << ThresholdX << endl;
cout << "Threshold X approx : " << ThresholdXestim << endl;
cout << "N of X " << SetX.size() << endl;
cout << "ThresholdY " << ThresholdY << endl;
//===========================================================================================================
// 2.LINK POINT
//===========================================================================================================
// Now that the threshold is found we will link the point by comparing each point
// Notice that if two point have the same X they can't be linked
// Create a vector holding the id of each peak
int ID = 0 ; // Initial Id of the first line
vector<int> vID(LinePos->size(), -1000);
// We want to loop on the UNIDENTIFIED peak.
for (int ipeak = 0 ; ipeak < LinePos->size() ; ipeak ++){
//Here do ID manipulation
//Create a set of ID without the unidentified peak
set<int> SetId(vID.begin() , vID.end());
if (SetId.size() > 1 ) {SetId.erase(-1000) ; ID = *prev(SetId.end()) + 1 ;}
//If the current peak is identified set newID to it's ID
if (vID.at(ipeak) != -1000) ID = vID.at(ipeak); // If the point is identified propagate it!
Double_t XPosPeak = LinePos->at(ipeak).at(0);
Double_t YPosPeak = LinePos->at(ipeak).at(1);
// Parse through all the next peak
// This could be done faster by sorting the list by X and Y before and stoping after a threshold
// For now it'll do just fine
for (int ipeakCompare = ipeak + 1 ; ipeakCompare < LinePos->size() ; ipeakCompare ++){
if ((vID.at(ipeakCompare) != -1000) || (XPosPeak == LinePos->at(ipeakCompare).at(0))) continue; // If the point is identified skip it !
//Calculate the distance between the two peak
Double_t XPosPeakComp = LinePos->at(ipeakCompare).at(0);
Double_t YPosPeakComp = LinePos->at(ipeakCompare).at(1);
Double_t dist = sqrt( pow(abs(XPosPeak-XPosPeakComp),2) + pow(abs(YPosPeak-YPosPeakComp),2) );
Double_t distX = sqrt( pow(abs(XPosPeak-XPosPeakComp),2) );
Double_t distY = sqrt( pow(abs(YPosPeak-YPosPeakComp),2) );
//If the distance is inferior to the threshold then set both ID to the new one !
if ((distX < ThresholdX )&& (distY <ThresholdY )) {
vID.at(ipeak) = ID ;
vID.at(ipeakCompare) = ID ;
} // end threshold if
} // end compare
} // end loop peak
//Check if needed
/*
for (int i=0 ; i<vID.size() ; i++){
if (vID.at(i)!=-1000){
cout << vID.at(i) << " , ";
}
}
*/
// Here we flush all the id that have less element than a threshold value
//===========================================================================================================
// 3. Flush line with not enough value
//===========================================================================================================
//This is a pretty sensitive variable
int MinElem = SetX.size()/4;
cout << "Min Elem " << MinElem << endl ;
set<int> SetIdTemp(vID.begin() , vID.end());
for (int Id : SetIdTemp){
int NId = 0 ;
for (int ipeak = 0 ; ipeak < vID.size() ; ipeak ++) {
if (vID.at(ipeak) == Id) NId += 1 ;
}
if ( NId < MinElem ) {
for (int ipeak = 0 ; ipeak < vID.size() ; ipeak ++) {
if (vID.at(ipeak) == Id) vID.at(ipeak) = -1000 ;
}
}
}
//===========================================================================================================
// 4. TCutG Creator
//===========================================================================================================
// We now have identified each peak to a id that correspond to a line in the DE E
// Now we need to link the ADJACENT line and then transform those point into tcutg
set<int> SetId(vID.begin() , vID.end()); // This is all the unique element of vID
vector<int> IndexPeak(LinePos->size());
for (int ipeak = 0 ; ipeak < LinePos->size() ; ipeak ++){
IndexPeak.at(ipeak) = ipeak ;
}
//===========================================================================================================
// 4.1 Sort by id
//===========================================================================================================
// The next part of the code SHOULD sort by meanY but does not.
// It therefore only serve as a convoluted way to sort by ID.
// We need to sort by id either way so no need to rewrite i
vector<Double_t> MeanYId(SetId.size()) ;
vector<Double_t> NelemYId(SetId.size()) ;
//Loop on the element of the set
for (int Id = 0 ; Id < SetId.size() ; Id ++){
Double_t Sum = 0 , Npeak = 1;
auto itSet = SetId.begin();
advance(itSet,Id);
//Loop on all the peak
for (int ipeak = 0 ; ipeak < LinePos->size() ; ipeak ++){
// If the ID of the peak correspond to one of the set then increment his Y and NPeak value
if (vID.at(ipeak) == *itSet )
{
Npeak += 1 ;
Sum += LinePos->at(ipeak).at(1);
} // End if Id
} // Loop on Peak
NelemYId.at(Id) = Npeak ;
MeanYId.at(Id) = Sum /Npeak ;
// cout << Id << " " << MeanYId.at(Id) << endl;
} // Loop on Id
//Now we will sort the MeanY vector and return a vector holding the ID sorted
vector<size_t> IndexMeanY(MeanYId.size());
iota(IndexMeanY.begin(),IndexMeanY.end(),0);
sort(IndexMeanY.begin(),IndexMeanY.end() , [&](size_t i, size_t j) {
return MeanYId.at(i) < MeanYId.at(j);
});
// Now we have to sort the SetId according to this index ordering
// Since set can't be sorted we need to copy it to a vector
vector<int> vSetId(SetId.begin(), SetId.end());
vector<int> SortedSetId(SetId.begin(), SetId.end());
for (int Index = 0 ; Index < IndexMeanY.size() ; Index ++){ SortedSetId.at(IndexMeanY.at(Index)) = vSetId.at(Index) ;}
/*
cout << endl << " SORTED" <<endl ;
for (int i = 0 ; i<IndexMeanY.size() ; i++){
if (IndexMeanY.at(i) == 49 || IndexMeanY.at(i) == 32){
cout << NelemYId.at(IndexMeanY.at(i)) << endl;
}
cout << IndexMeanY.at(i) << " " << SortedSetId.at(i) << " " << MeanYId.at(IndexMeanY.at(i)) << endl;
}
cout << endl << " *********************" <<endl ;
*/
//===========================================================================================================
// 4.2 Recompacting data into a struct
//===========================================================================================================
// We now have the different lines sorted by Id we just need to link them together by group of two.
// to do so we will sort each id by x and then link them
// 1st step is to sort the original IndexPeak according to the MeanY sort
// To do so we need to sort the vID vector according to meanY and apply this transformation to IndexPeak too
// vID as the ID of each peak -> correspondance of peak to a line
// IndexPeak -> index of each of these peak of size number of peak
// SortedSetId -> Set of sorted ID of peak according to mean y
//Create a map for the sort of ID
unordered_map<int,int> order_map ;
for (size_t i = 0 ; i < SortedSetId.size() ; i++ ) {
order_map[SortedSetId[i]] = i ;
}
// Combine Index and ID into a single vector of pair
vector<pair<int,int>> PeakId ;
for(size_t i = 0 ; i < IndexPeak.size() ; i++){
PeakId.emplace_back(IndexPeak.at(i), vID.at(i));
}
// Sort the PeakId on the custom order of IDs
sort(PeakId.begin(), PeakId.end(), [&order_map](const auto& a, const auto& b){
return order_map[a.second] < order_map[b.second];
} );
//2nd step is to take the original Position vector and sort it thanks to PeakId
vector<vector<Double_t>> LinePosSorted = *LinePos ;
//Swap LinePosSorted elemn according to Index of PeakID
for (int i = 0 ; i < PeakId.size() ; i++ ){
LinePosSorted.at(i) = LinePos->at(PeakId.at(i).first);
}
// 3rd step is to compile x y id and index in one structure
if (PeakId.size() != LinePos->size() ){
cerr << "Vector not same size" << endl;
}
vector<PeakData> PeakStruct ;
for (size_t i=0 ; i<LinePosSorted.size() ; i++){
PeakData TempPeak = {LinePosSorted.at(i).at(0), LinePosSorted.at(i).at(1) , PeakId.at(i).second , PeakId.at(i).first};
PeakStruct.push_back(TempPeak);
}
//===========================================================================================================
// 4.3 Cleanupi and sortY
//===========================================================================================================
// We drop all non clustered element and sort the whole struct by the mean value of Y for their ID.
// 4th step is to drop all element = -1000
//
auto groupBegin = find_if(PeakStruct.begin(),PeakStruct.end(),[&](const PeakData& elem){
return elem.LineId == -1000 ;
});
auto groupEnd = find_if(groupBegin,PeakStruct.end(),[&](const PeakData& elem){
return elem.LineId != -1000 ;
});
PeakStruct.erase(groupBegin,groupEnd);
//4.1 step we will resort by mean y because it didnt work before , probably due to data structure
auto ItMeanY = PeakStruct.begin();
vector<int> SortedId ;
vector<Indexed2D> MeanY ;
int index = 0;
while (ItMeanY != PeakStruct.end()){
//Get position of end of the current id
auto IdEnd = find_if(ItMeanY, PeakStruct.end() , [&](const PeakData& elem){
return elem.LineId != ItMeanY->LineId;
});
// Fill MeanY of the current Id
Double_t Sum = 0;
Double_t Npts = 0;
for (auto i = ItMeanY ; i!=IdEnd ; i++){
Sum += i->Y;
Npts += 1;
}
MeanY.push_back({Sum/Npts ,ItMeanY->LineId , index });
index +=1;
ItMeanY = IdEnd;
}
// We now have a all the mean Y of each ID and their index
// Let's now sort the id from this meanY
sort(MeanY.begin(),MeanY.end(),[&](Indexed2D a , Indexed2D b){
return a.x < b.x;
});
unordered_map<int,size_t> ordered_map ;
for (size_t i=0 ; i<MeanY.size() ; i++){
ordered_map[MeanY.at(i).id]=i;
}
sort(PeakStruct.begin(),PeakStruct.end(), [&ordered_map](const PeakData& a , const PeakData &b) {
return ordered_map[a.LineId]<ordered_map[b.LineId];
});
//===========================================================================================================
// 4.4 Sort by X alternating order
//===========================================================================================================
// We sort by X in alternating order because a TCutG need the point to be aligned to link them together
// Iterate and sort groups with the same LineId
auto it = PeakStruct.begin();// create an iterator that point to the elements of the PeakData
bool Oddness = true; // to know in which way sort
while (it != PeakStruct.end()){
// Find range where id is the same
// This return the first iterator where the condition is fulfilled
auto groupEnd = find_if(it,PeakStruct.end(),[lineId = it->LineId](const PeakData& elem){
return elem.LineId != lineId ;
});
// Now we sort this whole group by x
sort(it,groupEnd,[Oddness](const PeakData& a , const PeakData& b){
if (Oddness) return a.X < b.X ;
if (!Oddness) return a.X > b.X ;
else return a.X < b.X;
});
// Move to next group
it = groupEnd;
Oddness = not(Oddness) ;
}
// works !
/*
for (int i : SortedSetId) {cout << i << endl;}
int tempId= -1000;
for (PeakData i : PeakStruct) {
if (tempId != i.LineId ){
tempId = i.LineId;
cout << " Id " << i.LineId << " Y " << i.Y << endl;
}
}
*/
//===========================================================================================================
// 4.5 Create TCut
//===========================================================================================================
// We need to make sublist for each 2 lines
// We will iterate through our data and create substructure for each adjacent pair of LineID
// and store them in a vector of TCutG
vector<TCutG*> Res ;
it = PeakStruct.begin();
int iteration = 0;
while(it != PeakStruct.end()){
// 1st line
int LineIdBottom = it->LineId;
auto BottomLine = find_if(it,PeakStruct.end(), [&] (const PeakData& elem){
return elem.LineId != LineIdBottom ;
}) ;
//2nd line
int LineIdTop = BottomLine->LineId;
auto TopLine = find_if(BottomLine,PeakStruct.end(), [&] (const PeakData& elem){
return elem.LineId != LineIdTop ;
}) ;
//cout << LineIdBottom << " " << LineIdTop << endl;
// first we need to find the shortest line
int SizeBottom = distance(it,BottomLine);
int SizeTop = distance(BottomLine,TopLine);
//Subvector with the longest line first then the sortest
vector<PeakData> CutVec;
vector<PeakData> LongVec;
vector<PeakData> ShortVec;
if(SizeBottom >= SizeTop ){
CutVec.insert(CutVec.end(),it,TopLine);
LongVec.insert(LongVec.end(),it,BottomLine);
ShortVec.insert(ShortVec.end(),BottomLine,TopLine);
}
else {
CutVec.insert(CutVec.end(),BottomLine,TopLine);
CutVec.insert(CutVec.end(),it,BottomLine);
LongVec.insert(LongVec.end(),BottomLine,TopLine);
ShortVec.insert(ShortVec.end(),it,BottomLine);
}
//nbr of point
int Npoints = CutVec.size();
//Close the loop by finding the closest point to the last one
int closestIndexLast = 0;
int closestIndexBegin = LongVec.size()-1;
int SizeArray = 0;
if (ShortVec.size() == 0) {
closestIndexLast = CutVec.size();
SizeArray = CutVec.size();
}
else{
PeakData lastPoint = ShortVec.back();
double minDistance = distancePeak(lastPoint,LongVec.at(0));
for (size_t i = 1 ; i < LongVec.size(); i++){
double dist = distancePeak(lastPoint,LongVec.at(i));
if(dist<minDistance){
minDistance = dist;
closestIndexLast = i;
}
}
PeakData firstPoint = ShortVec.at(0);
double minDistanceFirst = distancePeak(firstPoint,LongVec.at(0));
for (size_t i = 1 ; i < LongVec.size(); i++){
double dist = distancePeak(firstPoint,LongVec.at(i));
if(dist<minDistanceFirst){
minDistanceFirst = dist;
closestIndexBegin = i;
}
}
SizeArray = ShortVec.size() + (closestIndexBegin - closestIndexLast);
}
//Create array to hold position
double *x = new double[SizeArray + 1 ];
double *y = new double[SizeArray + 1 ];
if (SizeTop == 0){
for(int i = 0 ; i<CutVec.size() ; i++) {
x[i] = CutVec.at(i).X ;
y[i] = CutVec.at(i).Y ;
}
x[SizeArray ] = CutVec.at(0).X;
y[SizeArray ] = CutVec.at(0).Y;
}
else {
for(int i = 0 ; i<ShortVec.size() ; i++) {
x[i] = ShortVec.at(i).X ;
y[i] = ShortVec.at(i).Y ;
}
for(int i = closestIndexLast ; i<=closestIndexBegin ; i++) {
x[i + ShortVec.size() - closestIndexLast] = LongVec.at(i).X ;
y[i + ShortVec.size() - closestIndexLast ] = LongVec.at(i).Y ;
}
x[SizeArray] = ShortVec.at(0).X;
y[SizeArray] = ShortVec.at(0).Y;
}
//Saving and moving on
if (Npoints >10 && (TopLine != PeakStruct.end())){ //Dont link last line to itself
TCutG* Cut = new TCutG(Form("Cut %d",iteration) , SizeArray+1, x , y);
Cut->SetLineWidth(2);
Cut->SetLineColor(kRed);
Res.push_back(Cut);
}
iteration += 1 ;
it = BottomLine ;
} // end while
return Res;
}
vector<Double_t> diff(const vector<Double_t>& input){
vector<Double_t> result;
if (input.size() < 2) return result; // Return empty vector if size < 2
for (size_t i = 1; i < input.size(); ++i) {
result.push_back(input[i] - input[i - 1]);
}
return result;
}
void SortAndRenameTCutG() {
TIter next(gROOT->GetListOfSpecials());
std::vector<std::pair<int, TCutG*>> cuts;
TObject *obj;
while ((obj = next())) {
TCutG *cut = dynamic_cast<TCutG*>(obj);
if (cut) {
std::string name = cut->GetName();
if (name.rfind("Cut", 0) == 0) { // Check if name starts with "Cut"
try {
int num = std::stoi(name.substr(3));
cuts.emplace_back(num, cut);
} catch (...) {
continue; // Ignore malformed names
}
}
}
}
// Sort by extracted number
std::sort(cuts.begin(), cuts.end(), [](const auto &a, const auto &b) {
return a.first < b.first;
});
// Rename sequentially
for (size_t i = 0; i < cuts.size(); ++i) {
cuts[i].second->SetName(Form("Cut %zu", i));
}
std::cout << "TCutG objects renamed from original numbering to 0:N.\n";
}
void SaveTCutGToFile() {
TFile file("Output/CutAutoZ.root", "RECREATE");
if (!file.IsOpen()) {
std::cerr << "Error: Could not open Output/CutAutoZ.root for writing.\n";
return;
}
TIter next(gROOT->GetListOfSpecials());
TObject *obj;
while ((obj = next())) {
TCutG *cut = dynamic_cast<TCutG*>(obj);
if (cut) {
cut->Write();
}
}
file.Close();
std::cout << "All TCutG objects saved to Output/CutAutoZ.root.\n";
}
double distancePeak(const PeakData& p1, const PeakData& p2) {
return abs(p1.X - p2.X);
}