Skip to content

Commit a569245

Browse files
committed
add qa task and copy of mcparticle datamodel
1 parent 2d280d9 commit a569245

File tree

6 files changed

+342
-71
lines changed

6 files changed

+342
-71
lines changed

ALICE3/Core/Decayer.h

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ class Decayer {
6666
if (!charge) {
6767
vx = pos[0] + rxyz * (mom[0] / track.getP());
6868
vy = pos[1] + rxyz * (mom[1] / track.getP());
69-
vy = pos[2] + rxyz * (mom[2] / track.getP());
69+
vz = pos[2] + rxyz * (mom[2] / track.getP());
7070
px = mom[0];
7171
py = mom[2];
7272
} else {
@@ -126,7 +126,6 @@ class Decayer {
126126
}
127127

128128

129-
// Setters
130129
void setSeed(const int seed)
131130
{
132131
mRand3.SetSeed(seed); // For decay length sampling
@@ -135,10 +134,6 @@ class Decayer {
135134

136135
void setBField(const double b) { mBz = b; }
137136

138-
// Getters
139-
140-
141-
142137
private:
143138
TRandom3 mRand3;
144139
double mBz;

ALICE3/Core/TrackUtilities.h

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,22 @@ void convertTLorentzVectorToO2Track(const int charge,
3939
o2::track::TrackParCov& o2track);
4040

4141
struct OTFParticle {
42-
int pdgCode;
43-
float e;
44-
float vx, vy, vz;
45-
float px, py, pz;
46-
47-
void setPDG(int pdg) { pdgCode = pdg; }
48-
void setVxVyVz(float _vx, float _vy, float _vz) { vx = _vx; vy = _vy; vz = _vz; }
49-
void setPxPyPzE(float _px, float _py, float _pz, float _e) { px = _px; py = _py; pz = _pz; e = _e; }
42+
int mPdgCode;
43+
float mE;
44+
float mVx, mVy, mVz;
45+
float mPx, mPy, mPz;
46+
47+
void setPDG(int pdg) { mPdgCode = pdg; }
48+
void setVxVyVz(float _vx, float _vy, float _vz) { mVx = _vx; mVy = _vy; mVz = _vz; }
49+
void setPxPyPzE(float _px, float _py, float _pz, float _e) { mPx = _px; mPy = _py; mPz = _pz; mE = _e; }
50+
int pdgCode() const { return mPdgCode; }
51+
float vx() const { return mVx; }
52+
float vy() const { return mVy; }
53+
float vz() const { return mVz; }
54+
float px() const { return mPx; }
55+
float py() const { return mPy; }
56+
float pz() const { return mPz; }
57+
float e() const { return mE; }
5058
};
5159

5260
/// Function to convert a TLorentzVector into a perfect Track
@@ -77,15 +85,9 @@ void convertTLorentzVectorToO2Track(int pdgCode,
7785
template <typename PdgService>
7886
void convertOTFParticleToO2Track(const OTFParticle& particle, o2::track::TrackParCov& o2track, const PdgService& pdg)
7987
{
80-
const auto pdgInfo = pdg->GetParticle(particle.pdgCode);
81-
int charge = 0;
82-
if (pdgInfo != nullptr) {
83-
charge = pdgInfo->Charge() / 3;
84-
}
85-
8688
static TLorentzVector tlv;
87-
tlv.SetPxPyPzE(particle.px, particle.py, particle.pz, particle.e);
88-
convertTLorentzVectorToO2Track(particle.pdgCode, tlv, {particle.vx, particle.vy, particle.vz}, o2track, pdg);
89+
tlv.SetPxPyPzE(particle.px(), particle.py(), particle.pz(), particle.e());
90+
convertTLorentzVectorToO2Track(particle.pdgCode(), tlv, {particle.vx(), particle.vy(), particle.vz()}, o2track, pdg);
8991
}
9092

9193
/// Function to convert a McParticle into a perfect Track

ALICE3/DataModel/OTFMCParticle.h

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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+
///
13+
/// \file OTFMCParticle.h
14+
/// \author Jesper Karlsson Gumprecht
15+
/// \since 16/12/2025
16+
/// \brief
17+
///
18+
19+
#ifndef ALICE3_DATAMODEL_OTFMCPARTICLE_H_
20+
#define ALICE3_DATAMODEL_OTFMCPARTICLE_H_
21+
22+
// O2 includes
23+
#include "Framework/AnalysisDataModel.h"
24+
25+
namespace o2::aod
26+
{
27+
28+
namespace otfmcparticle
29+
{
30+
DECLARE_SOA_COLUMN(Phi, phi, float);
31+
DECLARE_SOA_COLUMN(Eta, eta, float);
32+
DECLARE_SOA_COLUMN(Pt, pt, float);
33+
DECLARE_SOA_COLUMN(P, p, float);
34+
DECLARE_SOA_COLUMN(Y, y, float);
35+
}
36+
37+
DECLARE_SOA_TABLE_FULL(McParticlesWithDau, "McParticlesWithDau", "AOD", "MCPARTICLEWITHDAU",
38+
o2::soa::Index<>,
39+
mcparticle::McCollisionId,
40+
mcparticle::PdgCode,
41+
mcparticle::StatusCode,
42+
mcparticle::Flags,
43+
mcparticle::MothersIds,
44+
mcparticle::DaughtersIdSlice,
45+
mcparticle::Weight,
46+
mcparticle::Px,
47+
mcparticle::Py,
48+
mcparticle::Pz,
49+
mcparticle::E,
50+
mcparticle::Vx,
51+
mcparticle::Vy,
52+
mcparticle::Vz,
53+
mcparticle::Vt,
54+
otfmcparticle::Phi,
55+
otfmcparticle::Eta,
56+
otfmcparticle::Pt,
57+
otfmcparticle::P,
58+
otfmcparticle::Y,
59+
mcparticle::PVector<mcparticle::Px, mcparticle::Py, mcparticle::Pz>,
60+
mcparticle::ProducedByGenerator<mcparticle::Flags>,
61+
mcparticle::FromBackgroundEvent<mcparticle::Flags>,
62+
mcparticle::GetGenStatusCode<mcparticle::Flags, mcparticle::StatusCode>,
63+
mcparticle::GetHepMCStatusCode<mcparticle::Flags, mcparticle::StatusCode>,
64+
mcparticle::GetProcess<mcparticle::Flags, mcparticle::StatusCode>,
65+
mcparticle::IsPhysicalPrimary<mcparticle::Flags>);
66+
67+
using McParticleWithDau = McParticlesWithDau::iterator;
68+
69+
} // namespace o2::aod
70+
71+
#endif // ALICE3_DATAMODEL_OTFMCPARTICLE_H_

ALICE3/TableProducer/OTF/onTheFlyDecayer.cxx

Lines changed: 125 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
#include <TPDGCode.h>
4141

4242
#include <string>
43+
#include <gsl/span>
44+
#include <span>
4345
#include <vector>
4446

4547
using namespace o2;
@@ -65,11 +67,11 @@ static const std::vector<int> pdgCodes{kK0Short,
6567
kOmegaPlusBar};
6668

6769
struct OnTheFlyDecayer {
68-
Produces<aod::OTFMCParticles> tableMcParticles;
70+
Produces<aod::McParticlesWithDau> tableMcParticlesWithDau;
6971
o2::upgrade::Decayer decayer;
70-
Service<o2::ccdb::BasicCCDBManager> ccdb;
7172
Service<o2::framework::O2DatabasePDG> pdgDB;
7273
HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject};
74+
std::map<int64_t, std::vector<o2::upgrade::OTFParticle>> mDecayDaughters;
7375

7476
Configurable<int> seed{"seed", 0, "Set seed for particle decayer"};
7577
Configurable<float> magneticField{"magneticField", 20., "Magnetic field (kG)"};
@@ -78,15 +80,12 @@ struct OnTheFlyDecayer {
7880
"Enable option for particle to be decayed: 0 - no, 1 - yes"};
7981

8082

81-
ConfigurableAxis axisRadius{"axisRadius", {1000, 0, 100}, "Radius"};
8283
ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.2f, 2.4f, 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, 3.6f, 3.8f, 4.0f, 4.4f, 4.8f, 5.2f, 5.6f, 6.0f, 6.5f, 7.0f, 7.5f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 17.0f, 19.0f, 21.0f, 23.0f, 25.0f, 30.0f, 35.0f, 40.0f, 50.0f}, "pt axis for QA histograms"};
8384

8485

8586
std::vector<int> mEnabledDecays;
8687
void init(o2::framework::InitContext&)
8788
{
88-
ccdb->setURL("http://alice-ccdb.cern.ch");
89-
ccdb->setTimestamp(-1);
9089
decayer.setSeed(seed);
9190
decayer.setBField(magneticField);
9291
for (int i = 0; i < kNumDecays; ++i) {
@@ -95,13 +94,13 @@ struct OnTheFlyDecayer {
9594
}
9695
}
9796

98-
histos.add("K0S/hGenK0S", "hGenK0S", kTH2D, {axisRadius, axisPt});
99-
histos.add("Lambda/hGenLambda", "hGenLambda", kTH2D, {axisRadius, axisPt});
100-
histos.add("AntiLambda/hGenAntiLambda", "hGenAntiLambda", kTH2D, {axisRadius, axisPt});
101-
histos.add("Xi/hGenXi", "hGenXi", kTH2D, {axisRadius, axisPt});
102-
histos.add("AntiXi/hGenAntiXi", "hGenAntiXi", kTH2D, {axisRadius, axisPt});
103-
histos.add("Omega/hGenOmega", "hGenOmega", kTH2D, {axisRadius, axisPt});
104-
histos.add("AntiOmega/hGenAntiOmega", "hGenAntiOmega", kTH2D, {axisRadius, axisPt});
97+
histos.add("K0S/hGenK0S", "hGenK0S", kTH1D, {axisPt});
98+
histos.add("Lambda/hGenLambda", "hGenLambda", kTH1D, {axisPt});
99+
histos.add("AntiLambda/hGenAntiLambda", "hGenAntiLambda", kTH1D, {axisPt});
100+
histos.add("Xi/hGenXi", "hGenXi", kTH1D, {axisPt});
101+
histos.add("AntiXi/hGenAntiXi", "hGenAntiXi", kTH1D, {axisPt});
102+
histos.add("Omega/hGenOmega", "hGenOmega", kTH1D, {axisPt});
103+
histos.add("AntiOmega/hGenAntiOmega", "hGenAntiOmega", kTH1D, {axisPt});
105104
}
106105

107106
bool canDecay(const int pdgCode)
@@ -111,56 +110,133 @@ struct OnTheFlyDecayer {
111110

112111
void process(aod::McCollision const&, aod::McParticles const& mcParticles)
113112
{
114-
for (const auto& particle : mcParticles) {
115-
if (!canDecay(particle.pdgCode())) {
116-
continue;
117-
}
118-
119-
o2::track::TrackParCov o2track;
120-
o2::upgrade::convertMCParticleToO2Track(particle, o2track, pdgDB);
121-
std::vector<o2::upgrade::OTFParticle> decayDaughters = decayer.decayParticle(pdgDB, o2track, particle.pdgCode());
122-
for (const auto& dau : decayDaughters) {
123-
o2::track::TrackParCov dauTrack;
124-
o2::upgrade::convertOTFParticleToO2Track(dau, dauTrack, pdgDB);
125-
if (canDecay(dau.pdgCode)) {
126-
std::vector<o2::upgrade::OTFParticle> cascadingDaughers = decayer.decayParticle(pdgDB, dauTrack, dau.pdgCode);
127-
for (const auto& daudau : cascadingDaughers) {
128-
decayDaughters.push_back(daudau);
113+
mDecayDaughters.clear();
114+
u_int64_t nStoredDaughters = 0;
115+
for (int64_t index{0}; index < mcParticles.size(); ++index) {
116+
const auto& particle = mcParticles.iteratorAt(index);
117+
std::vector<o2::upgrade::OTFParticle> decayDaughters;
118+
static constexpr int kMaxNestedDecays = 10;
119+
int nDecays = 0;
120+
if (canDecay(particle.pdgCode())) {
121+
o2::track::TrackParCov o2track;
122+
o2::upgrade::convertMCParticleToO2Track(particle, o2track, pdgDB);
123+
decayDaughters = decayer.decayParticle(pdgDB, o2track, particle.pdgCode());
124+
for (size_t idau{0}; idau < decayDaughters.size(); ++idau) {
125+
const auto& dau = decayDaughters[idau];
126+
o2::track::TrackParCov dauTrack;
127+
o2::upgrade::convertOTFParticleToO2Track(dau, dauTrack, pdgDB);
128+
if (canDecay(dau.pdgCode())) {
129+
std::vector<o2::upgrade::OTFParticle> cascadingDaughers = decayer.decayParticle(pdgDB, dauTrack, dau.pdgCode());
130+
for (const auto& daudau : cascadingDaughers) {
131+
decayDaughters.push_back(daudau);
132+
if (kMaxNestedDecays < ++nDecays) {
133+
LOG(error) << "Seemingly stuck trying to perpetually decay products from pdg: " << particle.pdgCode();
134+
}
135+
}
129136
}
130137
}
131-
}
132138

133-
if (decayDaughters.empty()) {
134-
LOG(error) << "Attempted to decay " << particle.pdgCode() << " but resulting vector of daugthers were empty";
135-
continue;
139+
if (decayDaughters.empty()) {
140+
LOG(error) << "Attempted to decay " << particle.pdgCode() << " but resulting vector of daugthers were empty";
141+
continue;
142+
}
136143
}
137144

138145

139-
const float decayRadius2D = std::hypot(decayDaughters[0].vx, decayDaughters[0].vy);
140-
std::cout << particle.pdgCode() << ": " << decayRadius2D << std::endl;
141146
if (particle.pdgCode() == kK0Short) {
142-
histos.fill(HIST("K0S/hGenK0S"), decayRadius2D, particle.pt());
147+
histos.fill(HIST("K0S/hGenK0S"), particle.pt());
143148
} else if (particle.pdgCode() == kLambda0) {
144-
histos.fill(HIST("Lambda/hGenLambda"), decayRadius2D, particle.pt());
149+
histos.fill(HIST("Lambda/hGenLambda"), particle.pt());
145150
} else if (particle.pdgCode() == kLambda0Bar) {
146-
histos.fill(HIST("AntiLambda/hGenAntiLambda"), decayRadius2D, particle.pt());
151+
histos.fill(HIST("AntiLambda/hGenAntiLambda"), particle.pt());
147152
} else if (particle.pdgCode() == kXiMinus) {
148-
histos.fill(HIST("Xi/hGenXi"), decayRadius2D, particle.pt());
153+
histos.fill(HIST("Xi/hGenXi"), particle.pt());
149154
} else if (particle.pdgCode() == kXiPlusBar) {
150-
histos.fill(HIST("AntiXi/hGenAntiXi"), decayRadius2D, particle.pt());
155+
histos.fill(HIST("AntiXi/hGenAntiXi"), particle.pt());
151156
} else if (particle.pdgCode() == kOmegaMinus) {
152-
histos.fill(HIST("Omega/hGenOmega"), decayRadius2D, particle.pt());
157+
histos.fill(HIST("Omega/hGenOmega"), particle.pt());
153158
} else if (particle.pdgCode() == kOmegaPlusBar) {
154-
histos.fill(HIST("AntiOmega/hGenAntiOmega"), decayRadius2D, particle.pt());
159+
histos.fill(HIST("AntiOmega/hGenAntiOmega"), particle.pt());
155160
}
156-
157-
for (size_t i = 0; i < decayDaughters.size(); ++i) {
158-
const o2::upgrade::OTFParticle& dau = decayDaughters[i];
159-
const bool isAlive = !canDecay(dau.pdgCode);
160-
tableMcParticles(particle.globalIndex(),
161-
isAlive, dau.pdgCode,
162-
dau.vx, dau.vy, dau.vz,
163-
dau.px, dau.py, dau.pz);
161+
162+
int daughtersIdSlice[2];
163+
if (canDecay(particle.pdgCode())) {
164+
daughtersIdSlice[0] = static_cast<int>(mcParticles.size() + nStoredDaughters);
165+
daughtersIdSlice[1] = static_cast<int>(mcParticles.size() + nStoredDaughters + decayDaughters.size());
166+
} else {
167+
daughtersIdSlice[0] = static_cast<int>(particle.daughtersIds()[0]);
168+
daughtersIdSlice[1] = static_cast<int>(particle.daughtersIds()[1]);
169+
}
170+
171+
std::span<const int> motherSpan(particle.mothersIds().data(), particle.mothersIds().size());
172+
mDecayDaughters.emplace(index, decayDaughters);
173+
nStoredDaughters += decayDaughters.size();
174+
175+
float phi = o2::constants::math::PI + std::atan2(-1.0f * particle.py(), -1.0f * particle.px());
176+
float eta; // Conditional as https://github.com/AliceO2Group/AliceO2/blob/dev/Framework/Core/include/Framework/AnalysisDataModel.h#L1922
177+
float pt = std::sqrt(particle.px() * particle.px() + particle.py() * particle.py());
178+
float p = std::sqrt(particle.px() * particle.px() + particle.py() * particle.py() + particle.pz() * particle.pz());
179+
float y; // Conditional as https://github.com/AliceO2Group/AliceO2/blob/dev/Framework/Core/include/Framework/AnalysisDataModel.h#L1943
180+
181+
if ((p - particle.pz()) < 1e-7f) {
182+
eta = (particle.pz() < 0.0f) ? -100.0f : 100.0f;
183+
} else {
184+
eta = 0.5f * std::log((p + particle.pz()) / (p - particle.pz()));
185+
}
186+
187+
if ((particle.e() - particle.pz()) < 1e-7f) {
188+
y = (particle.pz() < 0.0f) ? -100.0f : 100.0f;
189+
} else {
190+
y = 0.5f * std::log((particle.e() + particle.pz()) / (particle.e() - particle.pz()));
191+
}
192+
193+
// TODO: Particle status code
194+
// TODO: Expression columns
195+
tableMcParticlesWithDau(particle.mcCollisionId(), particle.pdgCode(), particle.statusCode(),
196+
particle.flags(), motherSpan, daughtersIdSlice, particle.weight(),
197+
particle.px(), particle.py(), particle.pz(), particle.e(),
198+
particle.vx(), particle.vy(), particle.vz(), particle.vt(),
199+
phi, eta, pt, p, y);
200+
}
201+
202+
int daughtersIdSlice[2] = {-1, -1};
203+
for (const auto& [index, decayDaughters] : mDecayDaughters) {
204+
for (const auto& dau : decayDaughters) {
205+
if (index >= mcParticles.size()) {
206+
LOG(warn) << "--- Index " << index << " out of bounds for mcParticles table of size " << mcParticles.size();
207+
continue;
208+
}
209+
210+
auto mother = mcParticles.iteratorAt(index);
211+
std::vector<int> motherIds = { static_cast<int>(index) };
212+
std::span<const int> motherSpan(motherIds.data(), motherIds.size());
213+
214+
float phi = o2::constants::math::PI + std::atan2(-1.0f * dau.py(), -1.0f * dau.px());
215+
float eta; // Conditional as https://github.com/AliceO2Group/AliceO2/blob/dev/Framework/Core/include/Framework/AnalysisDataModel.h#L1922
216+
float pt = std::sqrt(dau.px() * dau.px() + dau.py() * dau.py());
217+
float p = std::sqrt(dau.px() * dau.px() + dau.py() * dau.py() + dau.pz() * dau.pz());
218+
float y; // Conditional as https://github.com/AliceO2Group/AliceO2/blob/dev/Framework/Core/include/Framework/AnalysisDataModel.h#L1943
219+
220+
if ((p - dau.pz()) < 1e-7f) {
221+
eta = (dau.pz() < 0.0f) ? -100.0f : 100.0f;
222+
} else {
223+
eta = 0.5f * std::log((p + dau.pz()) / (p - dau.pz()));
224+
}
225+
226+
if ((dau.e() - dau.pz()) < 1e-7f) {
227+
y = (dau.pz() < 0.0f) ? -100.0f : 100.0f;
228+
} else {
229+
y = 0.5f * std::log((dau.e() + dau.pz()) / (dau.e() - dau.pz()));
230+
}
231+
232+
233+
// TODO: Particle status code
234+
// TODO: Expression columns
235+
tableMcParticlesWithDau(mother.mcCollisionId(), dau.pdgCode(), 1,
236+
mother.flags(), motherSpan, daughtersIdSlice, mother.weight(),
237+
dau.px(), dau.py(), dau.pz(), dau.e(),
238+
dau.vx(), dau.vy(), dau.vz(), mother.vt(),
239+
phi, eta, pt, p, y);
164240
}
165241
}
166242
}
@@ -170,4 +246,4 @@ struct OnTheFlyDecayer {
170246
WorkflowSpec defineDataProcessing(ConfigContext const& cfgc)
171247
{
172248
return WorkflowSpec{adaptAnalysisTask<OnTheFlyDecayer>(cfgc)};
173-
}
249+
}

ALICE3/Tasks/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,8 @@ o2physics_add_dpl_workflow(alice3-strangeness
8888
SOURCES alice3-strangeness.cxx
8989
PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore
9090
COMPONENT_NAME Analysis)
91+
92+
o2physics_add_dpl_workflow(alice3-decayer-qa
93+
SOURCES alice3DecayerQA.cxx
94+
PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore
95+
COMPONENT_NAME Analysis)

0 commit comments

Comments
 (0)