Analiza tracków
Nadszedł czas na napisanie trochę bardziej zaawansowanego programu do analizy. A w zasadzie całej klasy będącej taskiem. Oto założenia naszego programu:
- analiza liczy rozkład pT dla danego rodzaju cząstek, każda kolekcja cząstek jest traktowana jak osobny typ cząstek
- użytkownik może analizować dowolną liczbę rodzajów cząstek - niech ta liczba będzie równa N
- możliwe, że użytkownik doda kilka (M) kolekcji cząstek, wtedy dla każdego zderzenia liczymy M-kolekcji cząstek, musimy więc zapisać NxM histogramów z rozkładem pT
Nasza analiza bazuje na właściwościach cząstek, więc naszą bazową klasą powinna być Hal::TrackAna. Napiszemy kod na dwa sposoby (ale wynik analizy będą takie same).
- opcja pierwsza - każda kolekcja cząstek jest replikowana N-razy, mamy więc efektywnie NxM kolekcji cząstek i każda ma swój histogram
- opcja druga - nie replikujemy kolekcji
Kiedy użyć pierwszej opcji a kiedy drugiej? Poniżej połączenia kolekcji w opcji pierwszej:
oraz w opcji drugiej:
Na czym polega różnica? na liczbie kolekcji i połączeniach między nimi. Załóżmy że kolekcja zderzeń nr 0 to zderzenia centralne, a kolekcja 1 to zderzenia peryferyjne, zaś w przypadku kolekcji mamy kolejno pion, kaony i protony. Jak wygląda przepływ danych?
- w opcji pierwsze eventy z kolekcji 0 są źródłem cząstek dla kolekcji 0 1 i 2 a eventy z kolekcji 1 dla cząstek z kolekcji 3,4,5
- w opcji drugiej każda kolekcja eventów generuje cząstki dla każdej kolekcji
W praktyce oznacza to że jeśli np. kolekcja 0 to zderzenia centralne, a kolekcja 1 to zderzenia peryferyjne, zaś kolekcje cząstek to odpowiednio piony,kaony i protony to nasze histogramy będą zawierały następujące dane:
Opcja 1 | Histogram passed | Histogram failed |
Kolekcja cząstek nr 0 | piony ze zderzeń centralnych | nie-piony ze zderzeń centralnych |
Kolekcja cząstek nr 1 | kaony ze zderzeń centralnych | nie-kaony ze zderzeń centralnych |
Kolekcja cząstek nr 2 | protony ze zderzeń centralnych | nie-protony ze zderzeń centralnych |
Kolekcja cząstek nr 3 (kopia 0) | piony ze zderzeń peryferyjnych | nie-piony ze zderzeń peryferyjnych |
Kolekcja cząstek nr 4 (kopia 1) | kaony ze zderzeń peryferyjnych | nie-kaony ze zderzeń peryferyjnych |
Kolekcja cząstek nr 5 (kopia 2) | protony ze zderzeń peryferyjnych | nie-protony ze zderzeń peryferyjnych |
Opcja 2 | ||
Kolekcja cząstek nr 0 | wszystkie piony | wszystkie nie-piony |
Kolekcja cząstek nr 1 | wszystkie kaony | wszystkie nie-kaony |
Kolekcja cząstek nr 2 | wszystkie protony | wszystkie nie-protony |
Zacznę od opisu klasy, gdzie replikujemy cięcia cząstkowe (opcja pierwsza). Na samym końcu strony jest pełen kod więc skupię się tylko na wybranych metodach. Zacznę od opcji pierwszej z replikacja kolekcji cząstek.
Funkcja Init:
- wywołujemy metodę Init klasy nadrzędnej, generalnie inicjalizacja tasku z analizą to dużo rzeczy więc polecam robić inicjalizację klasy nadrzędnej a dopiero potem dodanych przez siebie rzeczy
- sprawdzamy czy mamy co najmniej jedną kolekcję cząstek i zderzeń, jeśli nie, to zwracamy kERROR i task nie będzie uruchomiony
- tworzymy HistogramAxisConf - to klasa opisująca osie histogramu
- tworzymy HistogramManager - to klasa będąca tablicą obiektów-histogramów
virtual Hal::Task::EInitFlag Init() { auto status = Hal::TrackAna::Init(); Int_t eventCol = fCutContainer->GetEventCollectionsNo(); Int_t trackCol = fCutContainer->GetTrackCollectionsNo(); if (eventCol == 0) return Hal::Task::EInitFlag::kERROR; if (trackCol == 0) return Hal::Task::EInitFlag::kERROR; std::vector<Hal::Histogramaxisconf> axisconf; Hal::HistogramAxisConf xAxis("pT [GeV/c]", fBins, fMin, fMax); Hal::HistogramAxisConf yAxis("N [tracks]", 0, 0, 0); axisconf.push_back(xAxis); axisconf.push_back(yAxis); fHistograms = new Hal::HistogramManager_1_1D<TH1D>(); fHistograms->Init(trackCol, axisconf, kTRUE); int mod = trackCol / eventCol; int count = 0; for (int i = 0; i < eventCol; i++) { for (int j = 0; j < mod; j++) { fHistograms->At(count++)->SetTitle(Form("%i %i", i, j)); } } return status; }
Kolejna funkcja to ProcessTrack, jest ona wywołna dla każdej cząstki, która przeszła cięcia. Tutaj wyliczamy pęd poprzeczny i wypełniamy nim histogram. Indeks histogramu to także numer aktualnie przeliczanej kolekcji cząstek.
protected: virtual void ProcessTrack() { double pt = fCurrentTrack->GetMomentum().Pt(); fHistograms->Fill(fCurrentTrackCollectionID, pt); }
Kolejna metoda to LinkConnections, ona definuje jak łączą się nasze kolekcje, ponieważ kolekcji cząstek mamy NxM, to każdą kolekcję zderzeń łączymy z M kolekcją cząstek. Zakomentowana linia robi to samo co pętla for iEvent - replikuje kolekcje cząstkowe N-razy.
virtual void LinkCollections() {//też protected Int_t eventCol = fCutContainer->GetEventCollectionsNo(); Int_t trackCol = fCutContainer->GetTrackCollectionsNo(); // fCutContainer->LinkCollections(Hal::ECutUpdate::kEvent, Hal::ECutUpdate::kTrack, // Hal::CutContainer::ELinkPolicy::kReplicateLast); Int_t mod = trackCol / eventCol; for (int iEvent = 0; iEvent < eventCol; iEvent++) { for (int iTrack = 0; iTrack < mod; iTrack++) { fCutContainer->LinkCollections(Hal::ECutUpdate::kEvent, iEvent, Hal::ECutUpdate::kTrack, iTrack + mod * iEvent); } } }
CheckCutContainersCollections sprawdza czy liczba kolekcji jest ok. Tutaj replikujemy nasze kolekcje cząstek N razy (liczba kolekcji zderzeń). Jest ona wywoływana przed LinkCollections.
virtual void CheckCutContainerCollections() {//protected Int_t eventCol = fCutContainer->GetEventCollectionsNo(); Int_t trackCol = fCutContainer->GetTrackCollectionsNo(); for (int iEvent = 1; iEvent < eventCol; iEvent++) { for (int iTrack = 0; iTrack < trackCol; iTrack++) { fCutContainer->ReplicateCollection(Hal::ECutUpdate::kTrack, iTrack, trackCol * iEvent + iTrack); } } }
Ostatnia metoda to Report. Tutaj tylko dodajemy obiekty za analizy do paczki danych. Paczka musi mieć określoną strukturę (inaczej wszystkie raporty czy dostęp do histogramów przez Hal:AnaFile mogą nie działać prawidłowo), dlatego nie tworzymy jej od zera, ale używamy paczki stworzonej przez klasę nadrzędną i tylko dorzucamy nasze histogramy. Paczka jest tym co fizycznie będzie zapisane w pliku wynikowym.
virtual Hal::Package* Report() const {//protected auto report = Hal::TrackAna::Report(); for (int i = 0; i < fHistograms->GetSize(); i++) { report->AddObject(fHistograms->At(i)->Clone()); } return report; }
W przypadku opcji drugiej niemal wszystko wygląda tak samo z kilkoma drobnymi różnicami, wynikają one z tego że inaczej trzeba przeliczać numery kolekcji i tak np. zmieniony Init wygląda tak:
virtual Hal::Task::EInitFlag Init() { auto status = Hal::TrackAna::Init(); Int_t eventCol = fCutContainer->GetEventCollectionsNo(); Int_t trackCol = fCutContainer->GetTrackCollectionsNo(); if (eventCol == 0) return Hal::Task::EInitFlag::kERROR; if (trackCol == 0) return Hal::Task::EInitFlag::kERROR; std::vector<Hal::Histogramaxisconf> axisconf; Hal::HistogramAxisConf xAxis("pT [GeV/c]", fBins, fMin, fMax); Hal::HistogramAxisConf yAxis("N [tracks]", 0, 0, 0); axisconf.push_back(xAxis); axisconf.push_back(yAxis); fHistograms = new Hal::HistogramManager_1_1D<TH1D>(); fHistograms->Init(trackCol*eventCol, axisconf, kTRUE); int mod = trackCol / eventCol; int count = 0; for (int i = 0; i < eventCol; i++) { for (int j = 0; j < mod; j++) { fHistograms->At(count++)->SetTitle(Form("%i %i", i, j)); } } return status; }
W metodzie ProcessTrack również nieco inaczej trzeba przeliczać numer wypełnianego histogramu, bo mamy więcej histogramów (MxN) niż kolekcji cząstek (M). Jeśli wypełnimy histogram tak jak w poprzednim kodzie - używają jako indeksu fCurrentTrackCollectioID nie będziemy w stanie odróżnić cząstek z poszczególnych kolekcji zderzeń bo dla takiej samej kolekcji cząstek możemy mieć dane z różnych kolekcji zderzeń.
virtual void ProcessTrack() { double pt = fCurrentTrack->GetMomentum().Pt(); fHistograms->Fill(fTrackCollectionsNo * fCurrentEventCollectionID + fCurrentTrackCollectionID, pt); }
Linkowanie również trzeba zrobić inaczej, w tym przypadku użyłem innej metody, która automatycznie linkuje każdą kolekcję zderzeń z każdą kolekcję cząstek.
virtual void LinkCollections() { Int_t eventCol = fCutContainer->GetEventCollectionsNo(); Int_t trackCol = fCutContainer->GetTrackCollectionsNo(); Int_t mod = trackCol / eventCol; fCutContainer->LinkCollections(Hal::ECutUpdate::kEvent, Hal::ECutUpdate::kTrack, Hal::CutContainer::ELinkPolicy::kAnyToAny); }
Poniżej kompletny kod, dwa różne podejścia są przedstawione jako dwie różne klasy MyAna1 i MyAna2:
class MyAna : public Hal::TrackAna { Hal::HistogramManager_1_1D<TH1D>* fHistograms = {nullptr}; Double_t fMin = {0.0}, fMax = {1.0}; Int_t fBins = {100}; public: MyAna() {}; void ConfigAxis(Int_t bins, Double_t lo, Double_t hi) { fBins = bins; fMin = lo; fMax = hi; } virtual ~MyAna() { if (fHistograms) delete fHistograms; } virtual Hal::Task::EInitFlag Init() { auto status = Hal::TrackAna::Init(); Int_t eventCol = fCutContainer->GetEventCollectionsNo(); Int_t trackCol = fCutContainer->GetTrackCollectionsNo(); if (eventCol == 0) return Hal::Task::EInitFlag::kERROR; if (trackCol == 0) return Hal::Task::EInitFlag::kERROR; std::vector<Hal::HistogramAxisConf> axisconf; Hal::HistogramAxisConf xAxis("pT [GeV/c]", fBins, fMin, fMax); Hal::HistogramAxisConf yAxis("N [tracks]", 0, 0, 0); axisconf.push_back(xAxis); axisconf.push_back(yAxis); fHistograms = new Hal::HistogramManager_1_1D<TH1D>(); fHistograms->Init(trackCol, axisconf, kTRUE); int mod = trackCol / eventCol; int count = 0; for (int i = 0; i < eventCol; i++) { for (int j = 0; j < mod; j++) { fHistograms->At(count++)->SetTitle(Form("%i %i", i, j)); } } return status; } protected: virtual void ProcessTrack() { double pt = fCurrentTrack->GetMomentum().Pt(); fHistograms->Fill(fCurrentTrackCollectionID, pt); } virtual void LinkCollections() { Int_t eventCol = fCutContainer->GetEventCollectionsNo(); Int_t trackCol = fCutContainer->GetTrackCollectionsNo(); // fCutContainer->LinkCollections(Hal::ECutUpdate::kEvent, Hal::ECutUpdate::kTrack, // Hal::CutContainer::ELinkPolicy::kReplicateLast); Int_t mod = trackCol / eventCol; for (int iEvent = 0; iEvent < eventCol; iEvent++) { for (int iTrack = 0; iTrack < mod; iTrack++) { fCutContainer->LinkCollections(Hal::ECutUpdate::kEvent, iEvent, Hal::ECutUpdate::kTrack, iTrack + mod * iEvent); } } } virtual Hal::Package* Report() const { auto report = Hal::TrackAna::Report(); for (int i = 0; i < fHistograms->GetSize(); i++) { report->AddObject(fHistograms->At(i)->Clone()); } return report; } virtual void CheckCutContainerCollections() { Int_t eventCol = fCutContainer->GetEventCollectionsNo(); Int_t trackCol = fCutContainer->GetTrackCollectionsNo(); for (int iEvent = 1; iEvent < eventCol; iEvent++) { for (int iTrack = 0; iTrack < trackCol; iTrack++) { fCutContainer->ReplicateCollection(Hal::ECutUpdate::kTrack, iTrack, trackCol * iEvent + iTrack); } } } }; class MyAna2 : public Hal::TrackAna { Hal::HistogramManager_1_1D<TH1D>* fHistograms = {nullptr}; Double_t fMin = {0.0}, fMax = {1.0}; Int_t fBins = {100}; public: MyAna2() {}; void ConfigAxis(Int_t bins, Double_t lo, Double_t hi) { fBins = bins; fMin = lo; fMax = hi; } virtual ~MyAna2() { if (fHistograms) delete fHistograms; } virtual Hal::Task::EInitFlag Init() { auto status = Hal::TrackAna::Init(); Int_t eventCol = fCutContainer->GetEventCollectionsNo(); Int_t trackCol = fCutContainer->GetTrackCollectionsNo(); if (eventCol == 0) return Hal::Task::EInitFlag::kERROR; if (trackCol == 0) return Hal::Task::EInitFlag::kERROR; std::vector<Hal::HistogramAxisConf> axisconf; Hal::HistogramAxisConf xAxis("pT [GeV/c]", fBins, fMin, fMax); Hal::HistogramAxisConf yAxis("N [tracks]", 0, 0, 0); axisconf.push_back(xAxis); axisconf.push_back(yAxis); fHistograms = new Hal::HistogramManager_1_1D<TH1D>(); fHistograms->Init(eventCol * trackCol, axisconf, kTRUE); int mod = trackCol / eventCol; int count = 0; for (int i = 0; i < eventCol; i++) { for (int j = 0; j < mod; j++) { fHistograms->At(count++)->SetTitle(Form("%i %i", i, j)); } } return status; } protected: virtual void ProcessTrack() { double pt = fCurrentTrack->GetMomentum().Pt(); fHistograms->Fill(fTrackCollectionsNo * fCurrentEventCollectionID + fCurrentTrackCollectionID, pt); } virtual void LinkCollections() { Int_t eventCol = fCutContainer->GetEventCollectionsNo(); Int_t trackCol = fCutContainer->GetTrackCollectionsNo(); Int_t mod = trackCol / eventCol; fCutContainer->LinkCollections(Hal::ECutUpdate::kEvent, Hal::ECutUpdate::kTrack, Hal::CutContainer::ELinkPolicy::kAnyToAny); } virtual Hal::Package* Report() const { auto report = Hal::TrackAna::Report(); for (int i = 0; i < fHistograms->GetSize(); i++) { report->AddObject(fHistograms->At(i)->Clone()); } return report; } }; void AddCuts(Hal::TrackAna* ana) { Hal::EventPhiCut phi; phi.SetMinMax(0, TMath::Pi()); ana->AddCut(phi, "{0}"); phi.SetMinMax(-TMath::Pi(), TMath::Pi()); ana->AddCut(phi, "{1}"); Hal::TrackPtCut pT; pT.SetMinMax(0, 0.25); ana->AddCut(pT, "{0}"); pT.SetMinMax(0.25, 0.5); ana->AddCut(pT, "{1}"); pT.SetMinMax(0.5, 1.0); ana->AddCut(pT, "{2}"); } void track_ana() { auto run = new Hal::AnalysisManager(); auto source = new HalOTF::Source(10000); auto generator = new HalOTF::EventGenerator(); TH2D h("spec", "spec", 1, -1, 1, 1, 0, 1); h.SetBinContent(1, 1, 1); generator->SetSpiecies(h, Hal::Const::PionPlusPID()); generator->SetFixMult(100); source->AddEventGenerator(generator); source->Register(); run->SetOutput("/opt/temp/glauber.root"); run->SetSource(source); auto reader = new HalOTF::Reader(); run->AddTask(reader); MyAna* ana1 = new MyAna(); AddCuts(ana1); MyAna2* ana2 = new MyAna2(); AddCuts(ana2); run->AddTask(ana1); run->AddTask(ana2); run->Init(); run->Run(); }