Project
Loading...
Searching...
No Matches
test_EventDecayer.cxx
Go to the documentation of this file.
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#define BOOST_TEST_MODULE Test EventDecayer class
13#define BOOST_TEST_MAIN
14#define BOOST_TEST_DYN_LINK
15#include <boost/test/unit_test.hpp>
23#include <TParticle.h>
24#include <TRandom.h>
25#include <TMCProcess.h>
26#include <cmath>
27#include <fstream>
28#include <string>
29#include <unistd.h>
30#include <vector>
31
32using namespace o2::eventgen;
33
34namespace
35{
36
37// forces Xic+ -> Xi(1530)0 pi+ with the Geant4 lifetime
38const std::string FORCE = "4232:mayDecay = on\n4232:onMode = off\n4232:oneChannel = 1 1.0 0 3324 211\n4232:tau0 = 0.1358\n";
39
40// the default of EventDecayer.skipPdg
41const std::string DEFAULTSKIP = "115 225 335 10221 10331 20223 20333 102132 102134 9010221";
42
43// writes text to a fresh temporary file and returns its path
44std::string writeCfg(std::string const& text)
45{
46 char name[] = "/tmp/test_EventDecayer_XXXXXX";
47 int fd = mkstemp(name);
48 close(fd);
49 std::ofstream out(name);
50 out << text;
51 return name;
52}
53
54// a primary generator particle moving along z
55TParticle makeParticle(int pdg, double p, bool toBeDone, double vx = 0, double vy = 0, double vz = 0)
56{
57 bool ok = false;
58 double m = o2::O2DatabasePDG::Mass(pdg, ok);
59 double e = std::sqrt(p * p + m * m);
60 TParticle part(pdg, 1, -1, -1, -1, -1, 0., 0., p, e, vx, vy, vz, 0.);
62 part.SetUniqueID(kPPrimary);
63 return part;
64}
65
66int hepmc(TParticle const& p)
67{
68 return o2::mcgenstatus::getHepMCStatusCode(p.GetStatusCode());
69}
70
71bool toBeDone(TParticle const& p)
72{
73 return p.TestBit(ParticleStatus::kToBeDone);
74}
75
76// resets the parameter to the forcing configuration with nothing else set
77void setParams(std::string const& g4table = "", std::string const& extraCfg = "", int seed = 0)
78{
79 static const std::string forcePath = writeCfg(FORCE);
80 o2::conf::ConfigurableParam::setValue("EventDecayer.config[0]", "");
81 o2::conf::ConfigurableParam::setValue("EventDecayer.config[1]", forcePath);
82 o2::conf::ConfigurableParam::setValue("EventDecayer.config[2]", extraCfg);
83 o2::conf::ConfigurableParam::setValue("EventDecayer.g4ParticleTable", g4table);
84 o2::conf::ConfigurableParam::setValue("EventDecayer.seed", std::to_string(seed));
85 o2::conf::ConfigurableParam::setValue("EventDecayer.tau0Max", "10.");
86 o2::conf::ConfigurableParam::setValue("EventDecayer.skipPdg", DEFAULTSKIP);
87}
88
89// the output of decaying one Xic+ at (0.1,0.2,0.3) cm with p = 5 GeV
90std::vector<TParticle> decayOneXic(EventDecayerReport& report)
91{
92 setParams();
93 EventDecayer decayer;
94 BOOST_REQUIRE(decayer.init());
95 auto xic = makeParticle(4232, 5., true, 0.1, 0.2, 0.3);
96 // a distinctive generator status and a non-zero start time
98 xic.SetProductionVertex(0.1, 0.2, 0.3, 1e-9);
99 std::vector<TParticle> particles{xic};
100 report = decayer.decay(particles);
101 return particles;
102}
103
104bool sameParticle(TParticle const& a, TParticle const& b)
105{
106 return a.GetPdgCode() == b.GetPdgCode() && a.Px() == b.Px() && a.Py() == b.Py() && a.Pz() == b.Pz() &&
107 a.Energy() == b.Energy() && a.Vx() == b.Vx() && a.Vy() == b.Vy() && a.Vz() == b.Vz() && a.T() == b.T() &&
108 a.GetStatusCode() == b.GetStatusCode() && a.TestBits(0xffffffff) == b.TestBits(0xffffffff) &&
109 a.GetFirstMother() == b.GetFirstMother() && a.GetFirstDaughter() == b.GetFirstDaughter() &&
110 a.GetLastDaughter() == b.GetLastDaughter() && a.GetUniqueID() == b.GetUniqueID();
111}
112
113// checks that a decay call left the particles as they were
114void checkUnchanged(std::vector<TParticle> const& particles, std::vector<TParticle> const& before)
115{
116 BOOST_REQUIRE_EQUAL(particles.size(), before.size());
117 for (size_t i = 0; i < before.size(); ++i) {
118 BOOST_CHECK(sameParticle(particles[i], before[i]));
119 }
120}
121
122// a Xi- or Xi0, or their antiparticles
123bool isXi(TParticle const& p)
124{
125 return std::abs(p.GetPdgCode()) == 3312 || std::abs(p.GetPdgCode()) == 3322;
126}
127
128// 20000 Xic+ at p = 3 GeV decayed by one decayer, shared by the statistics cases
129std::vector<TParticle> const& manyXic()
130{
131 static std::vector<TParticle> particles;
132 if (particles.empty()) {
133 setParams();
134 EventDecayer decayer;
135 BOOST_REQUIRE(decayer.init());
136 for (int i = 0; i < 20000; ++i) {
137 particles.push_back(makeParticle(4232, 3., true));
138 }
139 auto report = decayer.decay(particles);
140 BOOST_REQUIRE(report.decayed[4232] == 20000);
141 }
142 return particles;
143}
144
145} // namespace
146
147BOOST_AUTO_TEST_CASE(xic_to_xistar_chain)
148{
150 auto particles = decayOneXic(report);
151
152 auto const& xic = particles[0];
153 BOOST_CHECK(!toBeDone(xic));
154 BOOST_CHECK_EQUAL(hepmc(xic), 2);
156
157 int nXiStar = 0;
158 int iXiStar = -1;
159 int iPi = -1;
160 for (int i = 1; i < (int)particles.size(); ++i) {
161 if (particles[i].GetPdgCode() == 3324) {
162 ++nXiStar;
163 iXiStar = i;
164 }
165 if (particles[i].GetPdgCode() == 211 && particles[i].GetFirstMother() == 0) {
166 iPi = i;
167 }
168 }
169 BOOST_REQUIRE_EQUAL(nXiStar, 1);
170 BOOST_REQUIRE(iPi > 0);
171
172 // the daughter range of every decayed particle holds exactly its direct products
173 for (int m = 0; m < (int)particles.size(); ++m) {
174 auto const& mother = particles[m];
175 int nChildren = 0;
176 for (auto const& p : particles) {
177 nChildren += p.GetFirstMother() == m;
178 }
179 if (hepmc(mother) != 2) {
180 BOOST_CHECK_EQUAL(nChildren, 0);
181 continue;
182 }
183 BOOST_CHECK_EQUAL(mother.GetLastDaughter() - mother.GetFirstDaughter() + 1, nChildren);
184 for (int d = mother.GetFirstDaughter(); d <= mother.GetLastDaughter(); ++d) {
185 BOOST_CHECK_EQUAL(particles[d].GetFirstMother(), m);
186 }
187 }
188 BOOST_CHECK_EQUAL(xic.GetLastDaughter() - xic.GetFirstDaughter() + 1, 2);
189
190 // products carry the Pythia decay status 91 and are flagged as primaries
191 for (int i = 1; i < (int)particles.size(); ++i) {
192 BOOST_CHECK_EQUAL(o2::mcgenstatus::getGenStatusCode(particles[i].GetStatusCode()), 91);
193 BOOST_CHECK_EQUAL(particles[i].GetUniqueID(), kPPrimary);
194 }
195 auto const& xiStar = particles[iXiStar];
196 BOOST_CHECK_EQUAL(xiStar.GetFirstMother(), 0);
197 BOOST_CHECK(!toBeDone(xiStar));
198 BOOST_CHECK_EQUAL(hepmc(xiStar), 2);
199
200 std::vector<int> dPdg;
201 for (int d = xiStar.GetFirstDaughter(); d >= 0 && d <= xiStar.GetLastDaughter(); ++d) {
202 dPdg.push_back(particles[d].GetPdgCode());
203 BOOST_CHECK(toBeDone(particles[d]));
204 BOOST_CHECK_EQUAL(hepmc(particles[d]), 1);
205 BOOST_CHECK_EQUAL(particles[d].GetFirstMother(), iXiStar);
206 }
207 BOOST_REQUIRE_EQUAL(dPdg.size(), 2);
208 bool charged = (dPdg[0] == 3312 && dPdg[1] == 211) || (dPdg[0] == 211 && dPdg[1] == 3312);
209 bool neutral = (dPdg[0] == 3322 && dPdg[1] == 111) || (dPdg[0] == 111 && dPdg[1] == 3322);
210 BOOST_CHECK(charged || neutral);
211
212 int first = xic.GetFirstDaughter();
213 int last = xic.GetLastDaughter();
214 BOOST_CHECK(first <= iXiStar && iXiStar <= last);
215 BOOST_CHECK(first <= iPi && iPi <= last);
216
217 // all products start at the Xic decay point
218 double dx = xiStar.Vx() - 0.1;
219 double dy = xiStar.Vy() - 0.2;
220 double dz = xiStar.Vz() - 0.3;
221 BOOST_CHECK(std::sqrt(dx * dx + dy * dy + dz * dz) > 0.);
222 for (int i = 1; i < (int)particles.size(); ++i) {
223 BOOST_CHECK_CLOSE(particles[i].Vx(), xiStar.Vx(), 1e-6);
224 BOOST_CHECK_CLOSE(particles[i].Vy(), xiStar.Vy(), 1e-6);
225 BOOST_CHECK_CLOSE(particles[i].Vz(), xiStar.Vz(), 1e-6);
226 }
227
228 // products start at the Xic time plus its flight time, |d| / (beta c) with c in cm/ns and T in s
229 double flightTime = std::sqrt(dx * dx + dy * dy + dz * dz) / (xic.P() / xic.Energy() * 29.9792458) * 1e-9;
230 for (int i = 1; i < (int)particles.size(); ++i) {
231 BOOST_CHECK_CLOSE(particles[i].T(), xic.T() + flightTime, 1e-6);
232 }
233 BOOST_CHECK_EQUAL(report.decayed[4232], 1);
234}
235
236BOOST_AUTO_TEST_CASE(antiparticle_chain)
237{
238 setParams();
239 EventDecayer decayer;
240 BOOST_REQUIRE(decayer.init());
241 std::vector<TParticle> particles{makeParticle(-4232, 5., true)};
242 auto report = decayer.decay(particles);
243 int nAntiXiStar = 0;
244 for (auto const& p : particles) {
245 nAntiXiStar += p.GetPdgCode() == -3324;
246 }
247 BOOST_CHECK_EQUAL(nAntiXiStar, 1);
248 BOOST_CHECK_EQUAL(report.decayed[-4232], 1);
249}
250
251BOOST_AUTO_TEST_CASE(proper_decay_length)
252{
253 auto const& particles = manyXic();
254 double sum = 0.;
255 int n = 0;
256 for (int i = 0; i < 20000; ++i) {
257 auto const& mother = particles[i];
258 auto const& daughter = particles[mother.GetFirstDaughter()];
259 double dx = daughter.Vx() - mother.Vx();
260 double dy = daughter.Vy() - mother.Vy();
261 double dz = daughter.Vz() - mother.Vz();
262 sum += std::sqrt(dx * dx + dy * dy + dz * dz) * mother.GetCalcMass() / mother.P();
263 ++n;
264 }
265 BOOST_CHECK_CLOSE(sum / n, 0.01358, 2.);
266}
267
268BOOST_AUTO_TEST_CASE(xistar_charge_split)
269{
270 auto const& particles = manyXic();
271 int nXiStar = 0;
272 int nCharged = 0;
273 for (auto const& p : particles) {
274 if (p.GetPdgCode() != 3324) {
275 continue;
276 }
277 ++nXiStar;
278 for (int d = p.GetFirstDaughter(); d >= 0 && d <= p.GetLastDaughter(); ++d) {
279 if (particles[d].GetPdgCode() == 3312) {
280 ++nCharged;
281 }
282 }
283 }
284 BOOST_REQUIRE_EQUAL(nXiStar, 20000);
285 BOOST_CHECK_SMALL(double(nCharged) / nXiStar - 0.667, 0.02);
286}
287
288BOOST_AUTO_TEST_CASE(strange_baryons_stay)
289{
290 setParams();
291 EventDecayer decayer;
292 BOOST_REQUIRE(decayer.init());
293 std::vector<TParticle> particles;
294 for (int pdg : {3312, 3122, 310, 3334}) {
295 particles.push_back(makeParticle(pdg, 2., true));
296 }
297 auto before = particles;
298 auto report = decayer.decay(particles);
299 checkUnchanged(particles, before);
300 BOOST_CHECK(report.decayed.empty());
301}
302
303BOOST_AUTO_TEST_CASE(nothing_to_decay)
304{
305 setParams();
306 EventDecayer decayer;
307 BOOST_REQUIRE(decayer.init());
308 std::vector<TParticle> particles;
309 for (int pdg : {211, -321, 2212, 22}) {
310 particles.push_back(makeParticle(pdg, 1., true, 0.01, 0.02, 0.03));
311 }
312 auto before = particles;
313 decayer.decay(particles);
314 checkUnchanged(particles, before);
315}
316
317BOOST_AUTO_TEST_CASE(not_tobedone_untouched)
318{
319 setParams();
320 EventDecayer decayer;
321 BOOST_REQUIRE(decayer.init());
322 std::vector<TParticle> particles{makeParticle(4232, 5., false)};
323 auto before = particles;
324 auto report = decayer.decay(particles);
325 checkUnchanged(particles, before);
326 BOOST_CHECK(report.decayed.empty());
327}
328
329BOOST_AUTO_TEST_CASE(second_pass_is_noop)
330{
332 auto particles = decayOneXic(report);
333 auto before = particles;
334 EventDecayer decayer;
335 BOOST_REQUIRE(decayer.init());
336 auto second = decayer.decay(particles);
337 checkUnchanged(particles, before);
338 BOOST_CHECK(second.decayed.empty());
339}
340
341BOOST_AUTO_TEST_CASE(weight_propagates)
342{
343 setParams();
344 EventDecayer decayer;
345 BOOST_REQUIRE(decayer.init());
346 std::vector<TParticle> particles{makeParticle(4232, 5., true)};
347 particles[0].SetWeight(2.5);
348 decayer.decay(particles);
349 BOOST_REQUIRE(particles.size() > 1);
350 for (size_t i = 1; i < particles.size(); ++i) {
351 BOOST_CHECK_EQUAL(particles[i].GetWeight(), 2.5);
352 }
353}
354
355BOOST_AUTO_TEST_CASE(ions_and_unknown_to_pythia)
356{
357 setParams();
358 EventDecayer decayer;
359 BOOST_REQUIRE(decayer.init());
360 std::vector<TParticle> particles{makeParticle(1000020040, 5., true), makeParticle(1010010030, 5., true)};
361 auto before = particles;
362 auto report = decayer.decay(particles);
363 checkUnchanged(particles, before);
364 BOOST_CHECK(report.failed.find(1000020040) == report.failed.end());
365 BOOST_CHECK(report.failed.find(1010010030) == report.failed.end());
366}
367
368BOOST_AUTO_TEST_CASE(impossible_decay)
369{
370 setParams();
371 EventDecayer decayer;
372 BOOST_REQUIRE(decayer.init());
373 auto xic = makeParticle(4232, 5., true);
374 // a mass of 1 GeV, below the 3324 + 211 threshold
375 xic.SetMomentum(0., 0., 5., std::sqrt(25. + 1.0));
376 xic.SetCalcMass(1.0);
377 std::vector<TParticle> particles{xic};
378 auto before = particles;
379 auto report = decayer.decay(particles);
380 checkUnchanged(particles, before);
381 BOOST_CHECK_EQUAL(report.failed[4232], 1);
382 BOOST_CHECK(report.decayed.find(4232) == report.decayed.end());
383}
384
385BOOST_AUTO_TEST_CASE(vertex_limits_refused)
386{
387 for (auto limit : {"limitTau", "limitRadius", "limitCylinder"}) {
388 setParams("", writeCfg(std::string("ParticleDecays:") + limit + " = on\n"));
389 EventDecayer decayer;
390 BOOST_CHECK(decayer.init());
392 }
393 // the species-level cut of a config is kept when the tau0Max parameter is off
394 setParams("", writeCfg("ParticleDecays:limitTau0 = on\nParticleDecays:tau0Max = 0.05\n"));
395 o2::conf::ConfigurableParam::setValue("EventDecayer.tau0Max", "0");
396 EventDecayer decayer;
397 BOOST_REQUIRE(decayer.init());
399 std::vector<TParticle> particles{makeParticle(4232, 5., true)};
400 auto report = decayer.decay(particles);
401 BOOST_CHECK_EQUAL(particles.size(), 1);
402 BOOST_CHECK(report.decayed.empty());
403 BOOST_CHECK(report.failed.empty());
404 setParams();
405}
406
408{
409 auto table = writeCfg("# test table\n111 pi0 0 0 0\n211 pi+ 7804.5 0 1\n3312 xi- 49.1 0 1\n3322 xi0 87.1 0 1\n");
410 setParams(table);
411 EventDecayer decayer;
412 BOOST_REQUIRE(decayer.init());
413 std::vector<TParticle> particles{makeParticle(4232, 5., true)};
414 auto report = decayer.decay(particles);
415 BOOST_CHECK_EQUAL(report.decayed[4232], 1);
416 BOOST_CHECK(report.unknownInG4.empty());
417
418 auto unknown = makeParticle(211, 1., true);
419 unknown.SetPdgCode(9900001);
420 std::vector<TParticle> more{unknown};
421 report = decayer.decay(more);
422 BOOST_CHECK_EQUAL(report.unknownInO2[9900001], 1);
423 BOOST_CHECK_EQUAL(report.unknownInG4[9900001], 1);
424}
425
426BOOST_AUTO_TEST_CASE(lifetime_mismatch)
427{
428 auto table = writeCfg("4232 xi_c+ 0.1300 0 1\n");
429 setParams(table);
430 EventDecayer decayer;
431 BOOST_REQUIRE(decayer.init());
432 std::vector<TParticle> particles{makeParticle(4232, 5., true)};
433 auto report = decayer.decay(particles);
434 BOOST_REQUIRE(report.lifetimeMismatch.count(4232) == 1);
435 BOOST_CHECK_CLOSE(report.lifetimeMismatch[4232].first, 0.1358, 1e-6);
436 BOOST_CHECK_CLOSE(report.lifetimeMismatch[4232].second, 0.1300, 1e-6);
437}
438
439BOOST_AUTO_TEST_CASE(units_mm_and_mmc)
440{
441 // the same decay in cm and s, and in mm and mm/c (the Generator default units)
442 const double mmc = 3.3356410e-12;
443 std::vector<TParticle> inCm;
444 std::vector<TParticle> inMm;
445 for (bool mm : {false, true}) {
446 setParams("", "", 4711);
447 EventDecayer decayer;
448 BOOST_REQUIRE(decayer.init());
449 auto xic = makeParticle(4232, 5., true);
450 if (mm) {
451 decayer.setUnits(0.1, mmc);
452 xic.SetProductionVertex(1., 2., 3., 1e-9 / mmc);
453 } else {
454 xic.SetProductionVertex(0.1, 0.2, 0.3, 1e-9);
455 }
456 auto& particles = mm ? inMm : inCm;
457 particles.push_back(xic);
458 decayer.decay(particles);
459 }
460 setParams();
461 BOOST_REQUIRE(inCm.size() > 1);
462 BOOST_REQUIRE_EQUAL(inCm.size(), inMm.size());
463 for (size_t i = 1; i < inCm.size(); ++i) {
464 BOOST_CHECK_CLOSE(inMm[i].Vz(), 10. * inCm[i].Vz(), 1e-6);
465 BOOST_CHECK_CLOSE(inMm[i].T(), inCm[i].T() / mmc, 1e-6);
466 }
467 // in mm and mm/c the flight time is the decay length over beta
468 auto const& xic = inMm[0];
469 auto const& d = inMm[xic.GetFirstDaughter()];
470 double length = std::sqrt(std::pow(d.Vx() - 1., 2) + std::pow(d.Vy() - 2., 2) + std::pow(d.Vz() - 3., 2));
471 BOOST_CHECK(length > 0.);
472 BOOST_CHECK_CLOSE(d.T() - xic.T(), length / (xic.P() / xic.Energy()), 1e-4);
473}
474
476{
477 // decays one Xic with the given seed and returns the seed in use and the decay position
478 auto run = [](int seed) {
479 setParams("", "", seed);
480 EventDecayer decayer;
481 BOOST_REQUIRE(decayer.init());
482 std::vector<TParticle> particles{makeParticle(4232, 5., true)};
483 decayer.decay(particles);
484 BOOST_REQUIRE(particles.size() > 1);
485 return std::make_pair(decayer.seed(), particles[1].Vz());
486 };
487 // a seed above the Pythia8 maximum is taken modulo 900000001
488 auto large = run(1000000123);
489 auto reduced = run(100000122);
490 BOOST_CHECK_EQUAL(large.first, 100000122);
491 BOOST_CHECK_EQUAL(large.second, reduced.second);
492 // seed 0 derives the seed from gRandom, differently from GeneratorPythia8 which takes it unchanged
493 gRandom->SetSeed(777);
494 auto fromGRandom = run(0);
495 BOOST_CHECK(fromGRandom.first != 777);
496 BOOST_CHECK(fromGRandom.first >= 0 && fromGRandom.first <= 900000000);
497 auto explicitSeed = run(fromGRandom.first);
498 BOOST_CHECK_EQUAL(fromGRandom.second, explicitSeed.second);
499 gRandom->SetSeed(777);
500 BOOST_CHECK_EQUAL(run(0).first, fromGRandom.first);
501 gRandom->SetSeed(778);
502 BOOST_CHECK(run(0).first != fromGRandom.first);
503 BOOST_CHECK(run(1).second != run(2).second);
504 setParams();
505}
506
507// quarks, gluon, diquarks and the Pythia8 pseudo-particles 81-99
508bool isPartonic(int pdg)
509{
510 int a = std::abs(pdg);
511 bool diquark = a > 1100 && a < 5600 && a / 1000 < 6 && (a / 10) % 10 == 0 && (a / 100) % 10 <= a / 1000 && (a / 100) % 10 > 0 && (a % 10) % 2 == 1;
512 return (a >= 1 && a <= 8) || a == 21 || (a >= 81 && a <= 99) || diquark;
513}
514
515BOOST_AUTO_TEST_CASE(partonic_decay_never_transports_partons)
516{
517 // Xic+ -> s uu_1 with meMode 91; whether Pythia8 hadronises the partons depends on its version (8.318 does, 8.315 does not)
518 setParams("", writeCfg("4232:oneChannel = 1 1.0 91 3 2203\n"));
519 EventDecayer decayer;
520 BOOST_REQUIRE(decayer.init());
521 std::vector<TParticle> particles{makeParticle(4232, 5., true)};
522 auto before = particles;
523 auto report = decayer.decay(particles);
524 for (auto const& p : particles) {
525 BOOST_CHECK(!(toBeDone(p) && isPartonic(p.GetPdgCode())));
526 }
527 if (report.failed[4232] == 1) {
528 checkUnchanged(particles, before);
529 BOOST_CHECK(report.decayed.empty());
530 } else {
531 BOOST_CHECK_EQUAL(report.decayed[4232], 1);
532 BOOST_CHECK(!toBeDone(particles[0]));
533 BOOST_CHECK(particles.size() > 1);
534 }
535}
536
537BOOST_AUTO_TEST_CASE(failed_intermediate_decay_fails)
538{
539 // Xi(1530)0 -> Xi- p is closed, so Pythia8 cannot decay the intermediate
540 setParams("", writeCfg("3324:oneChannel = 1 1.0 0 3312 2212\n"));
541 EventDecayer decayer;
542 BOOST_REQUIRE(decayer.init());
543 std::vector<TParticle> particles{makeParticle(4232, 5., true)};
544 auto before = particles;
545 auto report = decayer.decay(particles);
546 checkUnchanged(particles, before);
547 BOOST_CHECK_EQUAL(report.failed[4232], 1);
548 BOOST_CHECK(report.decayed.empty());
549}
550
551BOOST_AUTO_TEST_CASE(config_fallback_to_decayerpythia8)
552{
553 static const std::string forcePath = writeCfg(FORCE);
554 auto clear = []() {
555 for (int i = 0; i < 8; ++i) {
556 o2::conf::ConfigurableParam::setValue("EventDecayer.config[" + std::to_string(i) + "]", "");
557 }
558 };
559 // with every EventDecayer.config empty the DecayerPythia8 configs are read
560 setParams();
561 clear();
562 o2::conf::ConfigurableParam::setValue("DecayerPythia8.config[1]", forcePath);
563 {
564 EventDecayer decayer;
565 BOOST_REQUIRE(decayer.init());
566 auto summary = decayer.configSummary();
567 BOOST_CHECK(summary.find("pythia8/decays/base.cfg;" + forcePath) != std::string::npos);
568 std::vector<TParticle> particles{makeParticle(4232, 5., true)};
569 BOOST_CHECK_EQUAL(decayer.decay(particles).decayed[4232], 1);
570 }
571 // an explicitly set EventDecayer.config wins over the DecayerPythia8 one of the same slot
572 auto other = writeCfg("4232:mayDecay = off\n");
573 o2::conf::ConfigurableParam::setValue("EventDecayer.config[1]", other);
574 {
575 EventDecayer decayer;
576 BOOST_REQUIRE(decayer.init());
577 auto summary = decayer.configSummary();
578 BOOST_CHECK(summary.find("pythia8/decays/base.cfg;" + other) != std::string::npos);
579 BOOST_CHECK(summary.find(forcePath) == std::string::npos);
580 std::vector<TParticle> particles{makeParticle(4232, 5., true)};
581 BOOST_CHECK(decayer.decay(particles).decayed.empty());
582 }
583 o2::conf::ConfigurableParam::setValue("DecayerPythia8.config[1]", "");
584 setParams();
585}
586
587BOOST_AUTO_TEST_CASE(tau0max_leaves_long_lived)
588{
589 // Xic+ (tau0 0.1358 mm) above tau0Max stays, and is not counted as failed
590 setParams();
591 o2::conf::ConfigurableParam::setValue("EventDecayer.tau0Max", "0.1");
592 {
593 EventDecayer decayer;
594 BOOST_REQUIRE(decayer.init());
595 std::vector<TParticle> particles{makeParticle(4232, 5., true)};
596 auto before = particles;
597 auto report = decayer.decay(particles);
598 checkUnchanged(particles, before);
599 BOOST_CHECK(report.decayed.empty());
600 BOOST_CHECK(report.failed.empty());
601 }
602 // a decayable product above tau0Max (Xi-, Xi0) is left for transport
603 setParams("", writeCfg("3312:mayDecay = on\n3322:mayDecay = on\n"));
604 {
605 EventDecayer decayer;
606 BOOST_REQUIRE(decayer.init());
607 std::vector<TParticle> particles{makeParticle(4232, 5., true)};
608 auto report = decayer.decay(particles);
609 BOOST_CHECK_EQUAL(report.decayed[4232], 1);
610 int nXi = 0;
611 for (auto const& p : particles) {
612 if (isXi(p)) {
613 ++nXi;
614 BOOST_CHECK(toBeDone(p));
615 BOOST_CHECK_EQUAL(hepmc(p), 1);
616 }
617 }
618 BOOST_CHECK_EQUAL(nXi, 1);
619 }
620 // tau0Max = 0 switches the cut off, so the Xi decays as well
621 o2::conf::ConfigurableParam::setValue("EventDecayer.tau0Max", "0");
622 {
623 EventDecayer decayer;
624 BOOST_REQUIRE(decayer.init());
625 std::vector<TParticle> particles{makeParticle(4232, 5., true)};
626 decayer.decay(particles);
627 for (auto const& p : particles) {
628 if (isXi(p)) {
629 BOOST_CHECK_EQUAL(hepmc(p), 2);
630 }
631 }
632 }
633 setParams();
634}
635
637{
638 // a listed code is never decayed, for the particle and its antiparticle
639 setParams();
640 o2::conf::ConfigurableParam::setValue("EventDecayer.skipPdg", "421 4232");
641 {
642 EventDecayer decayer;
643 BOOST_REQUIRE(decayer.init());
644 std::vector<TParticle> particles{makeParticle(4232, 5., true), makeParticle(-4232, 5., true)};
645 auto before = particles;
646 auto report = decayer.decay(particles);
647 checkUnchanged(particles, before);
648 BOOST_CHECK(report.decayed.empty());
649 BOOST_CHECK(report.failed.empty());
650 }
651 // a listed product stays undecayed and is left for transport
652 o2::conf::ConfigurableParam::setValue("EventDecayer.skipPdg", "3324");
653 {
654 EventDecayer decayer;
655 BOOST_REQUIRE(decayer.init());
656 std::vector<TParticle> particles{makeParticle(4232, 5., true)};
657 auto report = decayer.decay(particles);
658 BOOST_CHECK_EQUAL(report.decayed[4232], 1);
659 BOOST_REQUIRE_EQUAL(particles.size(), 3);
660 for (size_t i = 1; i < particles.size(); ++i) {
661 BOOST_CHECK(toBeDone(particles[i]));
662 BOOST_CHECK_EQUAL(hepmc(particles[i]), 1);
663 }
664 }
665 // the default list keeps the Lambda(1520) for its Geant4 decay modes in O2
666 setParams();
667 {
668 EventDecayer decayer;
669 BOOST_REQUIRE(decayer.init());
670 std::vector<TParticle> particles{makeParticle(102134, 2., true), makeParticle(-102134, 2., true)};
671 auto report = decayer.decay(particles);
672 BOOST_CHECK_EQUAL(particles.size(), 2);
673 BOOST_CHECK(report.decayed.empty());
674 }
675 // with an empty list Pythia8 decays it
676 o2::conf::ConfigurableParam::setValue("EventDecayer.skipPdg", "");
677 {
678 EventDecayer decayer;
679 BOOST_REQUIRE(decayer.init());
680 std::vector<TParticle> particles{makeParticle(102134, 2., true)};
681 auto report = decayer.decay(particles);
682 BOOST_CHECK_EQUAL(report.decayed[102134], 1);
683 }
684 setParams();
685}
686
687BOOST_AUTO_TEST_CASE(units_momentum_and_energy)
688{
689 // the same decay in GeV and in MeV
690 std::vector<TParticle> inGeV;
691 std::vector<TParticle> inMeV;
692 for (bool mev : {false, true}) {
693 setParams("", "", 4711);
694 EventDecayer decayer;
695 BOOST_REQUIRE(decayer.init());
696 auto xic = makeParticle(4232, 5., true);
697 if (mev) {
698 decayer.setUnits(1., 1., 1e-3, 1e-3);
699 xic.SetMomentum(1e3 * xic.Px(), 1e3 * xic.Py(), 1e3 * xic.Pz(), 1e3 * xic.Energy());
700 }
701 auto& particles = mev ? inMeV : inGeV;
702 particles.push_back(xic);
703 decayer.decay(particles);
704 }
705 setParams();
706 BOOST_REQUIRE(inGeV.size() > 1);
707 BOOST_REQUIRE_EQUAL(inGeV.size(), inMeV.size());
708 for (size_t i = 1; i < inGeV.size(); ++i) {
709 BOOST_CHECK_EQUAL(inMeV[i].GetPdgCode(), inGeV[i].GetPdgCode());
710 BOOST_CHECK_CLOSE(inMeV[i].Pz(), 1e3 * inGeV[i].Pz(), 1e-6);
711 BOOST_CHECK_CLOSE(inMeV[i].Energy(), 1e3 * inGeV[i].Energy(), 1e-6);
712 BOOST_CHECK_CLOSE(inMeV[i].Vz(), inGeV[i].Vz(), 1e-6);
713 }
714}
std::vector< double > sum
int32_t i
Utility functions for MC particles.
@ kToBeDone
static Double_t Mass(int pdg, bool &success, TDatabasePDG *db=O2DatabasePDG::Instance())
static void setValue(std::string const &mainkey, std::string const &subkey, T x)
Decays unstable generator particles with Pythia8 before transport, keeping the full chain.
void setUnits(double positionUnit, double timeUnit, double momentumUnit=1., double energyUnit=1.)
Sets the caller's units in cm, s, GeV/c and GeV, as the unit setters of Generator (default cm,...
EventDecayerReport decay(std::vector< TParticle > &particles)
std::string configSummary() const
static void encodeParticleStatusAndTracking(TParticle &particle, bool wanttracking=true)
Definition MCUtils.cxx:211
GLdouble n
Definition glcorearb.h:1982
const GLfloat * m
Definition glcorearb.h:4066
GLuint const GLchar * name
Definition glcorearb.h:781
GLint first
Definition glcorearb.h:399
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLuint GLsizei GLsizei * length
Definition glcorearb.h:790
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
void report(gsl::span< o2::InteractionTimeRecord > irs, int threshold, bool verbose)
int getGenStatusCode(MCGenStatusEncoding enc)
int getHepMCStatusCode(MCGenStatusEncoding enc)
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
Counts of what one or more EventDecayer::decay calls did, per PDG code.
std::map< int, long > decayed
VectorOfTObjectPtrs other
bool isPartonic(int pdg)
BOOST_AUTO_TEST_CASE(xic_to_xistar_chain)
BOOST_CHECK(tree)
vec clear()
BOOST_CHECK_EQUAL(triggersD.size(), triggers.size())