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
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error First event to Number of events to Number of iterations to Number of iterations to Number of initial iterations excluded from const char e
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error First event to Number of events to Number of iterations to Number of iterations to Number of initial iterations excluded from const char Directory with events to Events directory is and not inside events d
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())