Skip to content

Commit be2946c

Browse files
authored
[PWGLF] Add new analysis task for lambda spin correlation study using derived data (#11783)
1 parent 4e47704 commit be2946c

File tree

2 files changed

+326
-0
lines changed

2 files changed

+326
-0
lines changed

PWGLF/Tasks/Strangeness/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,3 +145,8 @@ o2physics_add_dpl_workflow(lambdajetpolarization
145145
SOURCES lambdaJetpolarization.cxx
146146
PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGJECore FastJet::FastJet FastJet::Contrib O2Physics::EventFilteringUtils
147147
COMPONENT_NAME Analysis)
148+
149+
o2physics_add_dpl_workflow(lambdaspincorrderived
150+
SOURCES lambdaspincorrderived.cxx
151+
PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore
152+
COMPONENT_NAME Analysis)
Lines changed: 321 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
1+
// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2+
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3+
// All rights not expressly granted are reserved.
4+
//
5+
// This software is distributed under the terms of the GNU General Public
6+
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7+
//
8+
// In applying this license CERN does not waive the privileges and immunities
9+
// granted to it by virtue of its status as an Intergovernmental Organization
10+
// or submit itself to any jurisdiction.
11+
12+
/// \file taskLambdaSpinCorr.cxx
13+
/// \brief Analysis task for Lambda spin spin correlation
14+
///
15+
/// \author sourav.kundu@cern.ch
16+
17+
#include "PWGLF/DataModel/LFSpincorrelationTables.h"
18+
19+
#include "Common/Core/trackUtilities.h"
20+
21+
#include "CommonConstants/PhysicsConstants.h"
22+
#include "Framework/ASoAHelpers.h"
23+
#include "Framework/AnalysisDataModel.h"
24+
#include "Framework/AnalysisTask.h"
25+
#include "Framework/StepTHn.h"
26+
#include "Framework/runDataProcessing.h"
27+
#include <Framework/Configurable.h>
28+
29+
#include <Math/GenVector/Boost.h>
30+
#include <Math/Vector3D.h>
31+
#include <Math/Vector4D.h>
32+
#include <TLorentzVector.h>
33+
#include <TMath.h>
34+
35+
#include <fairlogger/Logger.h>
36+
37+
#include <iostream>
38+
#include <iterator>
39+
#include <string>
40+
#include <vector>
41+
42+
using namespace o2;
43+
using namespace o2::framework;
44+
using namespace o2::framework::expressions;
45+
using namespace o2::soa;
46+
47+
struct lambdaspincorrderived {
48+
// event sel/////////
49+
Configurable<float> centMin{"centMin", 0, "Minimum Centrality"};
50+
Configurable<float> centMax{"centMax", 80, "Maximum Centrality"};
51+
52+
// Lambda selection ////////////
53+
Configurable<bool> usePDGM{"usePDGM", 1, "Use PDG mass"};
54+
Configurable<bool> checkDoubleStatus{"checkDoubleStatus", 0, "Check Double status"};
55+
Configurable<float> cosPA{"cosPA", 0.995, "Cosine Pointing Angle"};
56+
Configurable<float> radiusMin{"radiusMin", 3, "Minimum V0 radius"};
57+
Configurable<float> radiusMax{"radiusMax", 30, "Maximum V0 radius"};
58+
Configurable<float> dcaProton{"dcaProton", 0.1, "DCA Proton"};
59+
Configurable<float> dcaPion{"dcaPion", 0.2, "DCA Pion"};
60+
Configurable<float> dcaDaughters{"dcaDaughters", 1.0, "DCA between daughters"};
61+
Configurable<float> ptMin{"ptMin", 0.5, "V0 Pt minimum"};
62+
Configurable<float> ptMax{"ptMax", 3.0, "V0 Pt maximum"};
63+
Configurable<float> rapidity{"rapidity", 0.5, "Rapidity cut on lambda"};
64+
65+
// Event Mixing
66+
Configurable<int> nEvtMixing{"nEvtMixing", 10, "Number of events to mix"};
67+
ConfigurableAxis CfgVtxBins{"CfgVtxBins", {10, -10, 10}, "Mixing bins - z-vertex"};
68+
ConfigurableAxis CfgMultBins{"CfgMultBins", {8, 0.0, 80}, "Mixing bins - centrality"};
69+
Configurable<float> etaMix{"etaMix", 0.1, "Eta cut on event mixing"};
70+
Configurable<float> ptMix{"ptMix", 0.1, "Pt cut on event mixing"};
71+
Configurable<float> phiMix{"phiMix", 0.1, "Phi cut on event mixing"};
72+
Configurable<float> massMix{"massMix", 0.0028, "Masscut on event mixing"};
73+
74+
// THnsparse bining
75+
ConfigurableAxis configThnAxisInvMass{"configThnAxisInvMass", {50, 1.09, 1.14}, "#it{M} (GeV/#it{c}^{2})"};
76+
ConfigurableAxis configThnAxisR{"configThnAxisR", {80, 0.0, 8.0}, "#it{R}"};
77+
ConfigurableAxis configThnAxisPol{"configThnAxisPol", {80, 0.0, 8.0}, "cos#it{#theta *}"};
78+
ConfigurableAxis configThnAxisCentrality{"configThnAxisCentrality", {8, 0.0, 80.0}, "Centrality"};
79+
HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject};
80+
81+
void init(o2::framework::InitContext&)
82+
{
83+
histos.add("hCentrality", "Centrality distribution", kTH1F, {{configThnAxisCentrality}});
84+
85+
histos.add("hSparseLambdaLambda", "hSparseLambdaLambda", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisCentrality, configThnAxisR}, true);
86+
histos.add("hSparseLambdaAntiLambda", "hSparseLambdaAntiLambda", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisCentrality, configThnAxisR}, true);
87+
histos.add("hSparseAntiLambdaAntiLambda", "hSparseAntiLambdaAntiLambda", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisCentrality, configThnAxisR}, true);
88+
89+
histos.add("hSparseLambdaLambdaMixed", "hSparseLambdaLambdaMixed", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisCentrality, configThnAxisR}, true);
90+
histos.add("hSparseLambdaAntiLambdaMixed", "hSparseLambdaAntiLambdaMixed", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisCentrality, configThnAxisR}, true);
91+
histos.add("hSparseAntiLambdaAntiLambdaMixed", "hSparseAntiLambdaAntiLambdaMixed", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisCentrality, configThnAxisR}, true);
92+
}
93+
94+
template <typename T>
95+
bool selectionV0(T const& candidate)
96+
{
97+
if (candidate.v0Cospa() < cosPA) {
98+
return false;
99+
}
100+
if (checkDoubleStatus && candidate.doubleStatus()) {
101+
return false;
102+
}
103+
if (candidate.v0Radius() > radiusMax) {
104+
return false;
105+
}
106+
if (candidate.v0Radius() < radiusMin) {
107+
return false;
108+
}
109+
if (candidate.dcaBetweenDaughter() > dcaDaughters) {
110+
return false;
111+
}
112+
if (candidate.v0Status() == 0 && std::abs(candidate.dcaPositive()) < dcaProton && std::abs(candidate.dcaNegative()) < dcaPion) {
113+
return false;
114+
}
115+
if (candidate.v0Status() == 1 && std::abs(candidate.dcaPositive()) < dcaPion && std::abs(candidate.dcaNegative()) < dcaProton) {
116+
return false;
117+
}
118+
if (candidate.lambdaPt() < ptMin) {
119+
return false;
120+
}
121+
if (candidate.lambdaPt() > ptMax) {
122+
return false;
123+
}
124+
return true;
125+
}
126+
127+
template <typename T1, typename T2>
128+
bool checkKinematics(T1 const& candidate1, T2 const& candidate2)
129+
{
130+
if (std::abs(candidate1.lambdaPt() - candidate2.lambdaPt()) > ptMix) {
131+
return false;
132+
}
133+
if (std::abs(candidate1.lambdaEta() - candidate2.lambdaEta()) > etaMix) {
134+
return false;
135+
}
136+
if (std::abs(candidate1.lambdaPhi() - candidate2.lambdaPhi()) > phiMix) {
137+
return false;
138+
}
139+
if (std::abs(candidate1.lambdaMass() - candidate2.lambdaMass()) > massMix) {
140+
return false;
141+
}
142+
return true;
143+
}
144+
145+
void fillHistograms(int tag1, int tag2,
146+
const ROOT::Math::PtEtaPhiMVector& particle1, const ROOT::Math::PtEtaPhiMVector& particle2,
147+
const ROOT::Math::PtEtaPhiMVector& daughpart1, const ROOT::Math::PtEtaPhiMVector& daughpart2,
148+
double centrality, int datatype)
149+
{
150+
auto lambda1Mass = 0.0;
151+
auto lambda2Mass = 0.0;
152+
if (!usePDGM) {
153+
lambda1Mass = particle1.M();
154+
lambda2Mass = particle2.M();
155+
} else {
156+
lambda1Mass = o2::constants::physics::MassLambda;
157+
lambda2Mass = o2::constants::physics::MassLambda;
158+
}
159+
auto particle1Dummy = ROOT::Math::PtEtaPhiMVector(particle1.Pt(), particle1.Eta(), particle1.Phi(), lambda1Mass);
160+
auto particle2Dummy = ROOT::Math::PtEtaPhiMVector(particle2.Pt(), particle2.Eta(), particle2.Phi(), lambda2Mass);
161+
auto pairDummy = particle1Dummy + particle2Dummy;
162+
ROOT::Math::Boost boostPairToCM{pairDummy.BoostToCM()}; // boosting vector for pair CM
163+
164+
// Step1: Boosting both Lambdas to Lambda-Lambda pair rest frame
165+
auto lambda1CM = boostPairToCM(particle1Dummy);
166+
auto lambda2CM = boostPairToCM(particle2Dummy);
167+
168+
// Step 2: Boost Each Lambda to its Own Rest Frame
169+
ROOT::Math::Boost boostLambda1ToCM{lambda1CM.BoostToCM()};
170+
ROOT::Math::Boost boostLambda2ToCM{lambda2CM.BoostToCM()};
171+
172+
// Also boost the daughter protons to the same frame
173+
auto proton1pairCM = boostPairToCM(daughpart1); // proton1 to pair CM
174+
auto proton2pairCM = boostPairToCM(daughpart2); // proton2 to pair CM
175+
176+
// Boost protons into their respective Lambda rest frames
177+
auto proton1LambdaRF = boostLambda1ToCM(proton1pairCM);
178+
auto proton2LambdaRF = boostLambda2ToCM(proton2pairCM);
179+
180+
auto cosThetaDiff = -999.0;
181+
cosThetaDiff = proton1LambdaRF.Vect().Unit().Dot(proton2LambdaRF.Vect().Unit());
182+
double deltaPhi = RecoDecay::constrainAngle(particle1Dummy.Phi() - particle2Dummy.Phi(), 0.0F, 2U);
183+
double deltaEta = particle1Dummy.Eta() - particle2Dummy.Eta();
184+
double deltaR = TMath::Sqrt(deltaEta * deltaEta + deltaPhi * deltaPhi);
185+
186+
if (datatype == 0) {
187+
if (tag1 == 0 && tag2 == 0) {
188+
histos.fill(HIST("hSparseLambdaLambda"), particle1.M(), particle2.M(), cosThetaDiff, centrality, deltaR);
189+
} else if ((tag1 == 0 && tag2 == 1) || (tag1 == 1 && tag2 == 0)) {
190+
histos.fill(HIST("hSparseLambdaAntiLambda"), particle1.M(), particle2.M(), cosThetaDiff, centrality, deltaR);
191+
} else if (tag1 == 1 && tag2 == 1) {
192+
histos.fill(HIST("hSparseAntiLambdaAntiLambda"), particle1.M(), particle2.M(), cosThetaDiff, centrality, deltaR);
193+
}
194+
} else if (datatype == 1) {
195+
if (tag1 == 0 && tag2 == 0) {
196+
histos.fill(HIST("hSparseLambdaLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, centrality, deltaR);
197+
} else if ((tag1 == 0 && tag2 == 1) || (tag1 == 1 && tag2 == 0)) {
198+
histos.fill(HIST("hSparseLambdaAntiLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, centrality, deltaR);
199+
} else if (tag1 == 1 && tag2 == 1) {
200+
histos.fill(HIST("hSparseAntiLambdaAntiLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, centrality, deltaR);
201+
}
202+
}
203+
}
204+
205+
ROOT::Math::PtEtaPhiMVector lambda0, proton0;
206+
ROOT::Math::PtEtaPhiMVector lambda, proton;
207+
ROOT::Math::PtEtaPhiMVector lambda2, proton2;
208+
209+
Filter centralityFilter = (nabs(aod::lambdaevent::cent) < centMax && nabs(aod::lambdaevent::cent) > centMin);
210+
211+
using EventCandidates = soa::Filtered<aod::LambdaEvents>;
212+
using AllTrackCandidates = aod::LambdaPairs;
213+
214+
void processData(EventCandidates::iterator const& collision, AllTrackCandidates const& V0s)
215+
{
216+
auto centrality = collision.cent();
217+
for (const auto& v0 : V0s) {
218+
if (!selectionV0(v0)) {
219+
continue;
220+
}
221+
proton = ROOT::Math::PtEtaPhiMVector(v0.protonPt(), v0.protonEta(), v0.protonPhi(), o2::constants::physics::MassProton);
222+
lambda = ROOT::Math::PtEtaPhiMVector(v0.lambdaPt(), v0.lambdaEta(), v0.lambdaPhi(), v0.lambdaMass());
223+
for (const auto& v02 : V0s) {
224+
if (v02.index() <= v0.index()) {
225+
continue;
226+
}
227+
if (!selectionV0(v02)) {
228+
continue;
229+
}
230+
if (v0.protonIndex() == v02.protonIndex()) {
231+
continue;
232+
}
233+
if (v0.pionIndex() == v02.pionIndex()) {
234+
continue;
235+
}
236+
proton2 = ROOT::Math::PtEtaPhiMVector(v02.protonPt(), v02.protonEta(), v02.protonPhi(), o2::constants::physics::MassProton);
237+
lambda2 = ROOT::Math::PtEtaPhiMVector(v02.lambdaPt(), v02.lambdaEta(), v02.lambdaPhi(), v02.lambdaMass());
238+
if (v0.v0Status() == 0 && v02.v0Status() == 0) {
239+
fillHistograms(0, 0, lambda, lambda2, proton, proton2, centrality, 0);
240+
}
241+
if (v0.v0Status() == 0 && v02.v0Status() == 1) {
242+
fillHistograms(0, 1, lambda, lambda2, proton, proton2, centrality, 0);
243+
}
244+
if (v0.v0Status() == 1 && v02.v0Status() == 0) {
245+
fillHistograms(1, 0, lambda2, lambda, proton2, proton, centrality, 0);
246+
}
247+
if (v0.v0Status() == 1 && v02.v0Status() == 1) {
248+
fillHistograms(1, 1, lambda, lambda2, proton, proton2, centrality, 0);
249+
}
250+
}
251+
}
252+
}
253+
PROCESS_SWITCH(lambdaspincorrderived, processData, "Process data", true);
254+
255+
// Processing Event Mixing
256+
SliceCache cache;
257+
using BinningType = ColumnBinningPolicy<aod::lambdaevent::Posz, aod::lambdaevent::Cent>;
258+
BinningType colBinning{{CfgVtxBins, CfgMultBins}, true};
259+
Preslice<aod::LambdaPairs> tracksPerCollisionV0 = aod::lambdapair::lambdaeventId;
260+
void processME(EventCandidates const& collisions, AllTrackCandidates const& V0s)
261+
{
262+
for (auto& [collision1, collision2] : selfCombinations(colBinning, nEvtMixing, -1, collisions, collisions)) {
263+
// LOGF(info, "Mixed event collisions: (%d, %d)", collision1.index(), collision2.index());
264+
auto centrality = collision1.cent();
265+
auto groupV01 = V0s.sliceBy(tracksPerCollisionV0, collision1.globalIndex());
266+
auto groupV02 = V0s.sliceBy(tracksPerCollisionV0, collision1.globalIndex());
267+
auto groupV03 = V0s.sliceBy(tracksPerCollisionV0, collision2.globalIndex());
268+
std::vector<bool> t1Used(groupV01.size(), false); // <-- reset here
269+
for (auto& [t1, t3] : soa::combinations(o2::soa::CombinationsFullIndexPolicy(groupV01, groupV03))) {
270+
if (t1Used[t1.index()]) {
271+
continue;
272+
}
273+
if (!checkKinematics(t1, t3)) {
274+
continue;
275+
}
276+
if (!selectionV0(t1)) {
277+
continue;
278+
}
279+
if (!selectionV0(t3)) {
280+
continue;
281+
}
282+
t1Used[t1.index()] = true;
283+
for (const auto& t2 : groupV02) {
284+
if (t2.index() <= t1.index()) {
285+
continue;
286+
}
287+
if (!selectionV0(t2)) {
288+
continue;
289+
}
290+
if (t1.protonIndex() == t2.protonIndex()) {
291+
continue;
292+
}
293+
if (t1.pionIndex() == t2.pionIndex()) {
294+
continue;
295+
}
296+
proton = ROOT::Math::PtEtaPhiMVector(t3.protonPt(), t3.protonEta(), t3.protonPhi(), o2::constants::physics::MassProton);
297+
lambda = ROOT::Math::PtEtaPhiMVector(t3.lambdaPt(), t3.lambdaEta(), t3.lambdaPhi(), t3.lambdaMass());
298+
proton2 = ROOT::Math::PtEtaPhiMVector(t2.protonPt(), t2.protonEta(), t2.protonPhi(), o2::constants::physics::MassProton);
299+
lambda2 = ROOT::Math::PtEtaPhiMVector(t2.lambdaPt(), t2.lambdaEta(), t2.lambdaPhi(), t2.lambdaMass());
300+
if (t3.v0Status() == 0 && t2.v0Status() == 0) {
301+
fillHistograms(0, 0, lambda, lambda2, proton, proton2, centrality, 1);
302+
}
303+
if (t3.v0Status() == 0 && t2.v0Status() == 1) {
304+
fillHistograms(0, 1, lambda, lambda2, proton, proton2, centrality, 1);
305+
}
306+
if (t3.v0Status() == 1 && t2.v0Status() == 0) {
307+
fillHistograms(1, 0, lambda2, lambda, proton2, proton, centrality, 1);
308+
}
309+
if (t3.v0Status() == 1 && t2.v0Status() == 1) {
310+
fillHistograms(1, 1, lambda, lambda2, proton, proton2, centrality, 1);
311+
}
312+
}
313+
} // replacement track pair
314+
} // collision pair
315+
}
316+
PROCESS_SWITCH(lambdaspincorrderived, processME, "Process data ME", true);
317+
};
318+
WorkflowSpec defineDataProcessing(ConfigContext const& cfgc)
319+
{
320+
return WorkflowSpec{adaptAnalysisTask<lambdaspincorrderived>(cfgc)};
321+
}

0 commit comments

Comments
 (0)