Thermal-FIST 1.5
Package for hadron resonance gas model applications
Loading...
Searching...
No Matches
ParticleDecaysMC.cpp
Go to the documentation of this file.
1/*
2 * Thermal-FIST package
3 *
4 * Copyright (c) 2015-2019 Volodymyr Vovchenko
5 *
6 * GNU General Public License (GPLv3 or later)
7 */
9
10#include "HRGBase/xMath.h"
12
13namespace thermalfist {
14
15 namespace ParticleDecaysMC {
16
17
18 double ThreeBodym12F2(double m12, double M, double m1, double m2, double m3) {
19 double m12k = m12 * m12;
20 return (m12k - (m1 + m2)*(m1 + m2)) * (m12k - (m1 - m2)*(m1 - m2)) * (M*M - (m12 + m3)*(m12 + m3)) * (M*M - (m12 - m3)*(m12 - m3)) / m12k;
21 }
22
23 // Ternary search for maximum of probability density for m12
24 double TernaryThreeBodym12Maximum(double M, double m1_, double m2_, double m3_) {
25 double l = m1_ + m2_, r = M - m3_;
26 double m1 = 0., m2 = 0.;
27 m1 = l + (r - l) / 3.;
28 m2 = r - (r - l) / 3.;
29 while ((m2 - m1) > 1e-8) {
30 double f1 = ThreeBodym12F2(m1, M, m1_, m2_, m3_);
31 double f2 = ThreeBodym12F2(m2, M, m1_, m2_, m3_);
32 if (f1 < f2) {
33 l = m1;
34 }
35 else {
36 r = m2;
37 }
38 m1 = l + (r - l) / 3.;
39 m2 = r - (r - l) / 3.;
40 }
41 return sqrt(ThreeBodym12F2((m1 + m2) / 2., M, m1_, m2_, m3_));
42 }
43
45
46 // Random sample for m12 in a 3-body decay
47 double GetRandomThreeBodym12(double M, double m1_, double m2_, double m3_, double fm12max) {
48 while (true) {
50 double x0 = m1_ + m2_ + (M - m1_ - m2_ - m3_) * RandomGenerators::randgenMT.randDblExc();
51 double y0 = fm12max * RandomGenerators::randgenMT.randDblExc();
52 if (y0*y0 < ThreeBodym12F2(x0, M, m1_, m2_, m3_)) {
54 return x0;
55 }
56 }
57 return 0.;
58 }
59
60 SimpleParticle LorentzBoost(const SimpleParticle &part, double vx, double vy, double vz) {
61 return LorentzBoostMomentaAndCoordinates(part, vx, vy, vz);
62 }
63
64 SimpleParticle LorentzBoostMomentumOnly(const SimpleParticle& part, double vx, double vy, double vz)
65 {
66 SimpleParticle ret = part;
67 double v2 = vx * vx + vy * vy + vz * vz;
68 if (v2 == 0.0)
69 return part;
70 double gamma = 1. / sqrt(1. - v2);
71 ret.p0 = gamma * part.p0 - gamma * (vx * part.px + vy * part.py + vz * part.pz);
72 ret.px = -gamma * vx * part.p0 + (1. + (gamma - 1.) * vx * vx / v2) * part.px +
73 (gamma - 1.) * vx * vy / v2 * part.py + (gamma - 1.) * vx * vz / v2 * part.pz;
74 ret.py = -gamma * vy * part.p0 + (1. + (gamma - 1.) * vy * vy / v2) * part.py +
75 (gamma - 1.) * vy * vx / v2 * part.px + (gamma - 1.) * vy * vz / v2 * part.pz;
76 ret.pz = -gamma * vz * part.p0 + (1. + (gamma - 1.) * vz * vz / v2) * part.pz +
77 (gamma - 1.) * vz * vx / v2 * part.px + (gamma - 1.) * vz * vy / v2 * part.py;
78
79
80 return ret;
81 }
82
83 SimpleParticle LorentzBoostMomentaAndCoordinates(const SimpleParticle& part, double vx, double vy, double vz)
84 {
85 SimpleParticle ret = part;
86 double v2 = vx * vx + vy * vy + vz * vz;
87 if (v2 == 0.0)
88 return part;
89 double gamma = 1. / sqrt(1. - v2);
90 ret.p0 = gamma * part.p0 - gamma * (vx * part.px + vy * part.py + vz * part.pz);
91 ret.px = -gamma * vx * part.p0 + (1. + (gamma - 1.) * vx * vx / v2) * part.px +
92 (gamma - 1.) * vx * vy / v2 * part.py + (gamma - 1.) * vx * vz / v2 * part.pz;
93 ret.py = -gamma * vy * part.p0 + (1. + (gamma - 1.) * vy * vy / v2) * part.py +
94 (gamma - 1.) * vy * vx / v2 * part.px + (gamma - 1.) * vy * vz / v2 * part.pz;
95 ret.pz = -gamma * vz * part.p0 + (1. + (gamma - 1.) * vz * vz / v2) * part.pz +
96 (gamma - 1.) * vz * vx / v2 * part.px + (gamma - 1.) * vz * vy / v2 * part.py;
97
98 ret.r0 = gamma * part.r0 - gamma * (vx * part.rx + vy * part.ry + vz * part.rz);
99 ret.rx = -gamma * vx * part.r0 + (1. + (gamma - 1.) * vx * vx / v2) * part.rx +
100 (gamma - 1.) * vx * vy / v2 * part.ry + (gamma - 1.) * vx * vz / v2 * part.rz;
101 ret.ry = -gamma * vy * part.r0 + (1. + (gamma - 1.) * vy * vy / v2) * part.ry +
102 (gamma - 1.) * vy * vx / v2 * part.rx + (gamma - 1.) * vy * vz / v2 * part.rz;
103 ret.rz = -gamma * vz * part.r0 + (1. + (gamma - 1.) * vz * vz / v2) * part.rz +
104 (gamma - 1.) * vz * vx / v2 * part.rx + (gamma - 1.) * vz * vy / v2 * part.ry;
105
106
107 return ret;
108 }
109
110 std::vector<SimpleParticle> TwoBodyDecay(const SimpleParticle & Mother, double m1, long long pdg1, double m2, long long pdg2) {
111
112 std::vector<SimpleParticle> ret(0);
113 ret.push_back(Mother);
114 ret.push_back(Mother);
115 ret[0].PDGID = pdg1;
116 ret[0].m = m1;
117 ret[1].PDGID = pdg2;
118 ret[1].m = m2;
119
120 double vx = Mother.px / Mother.p0;
121 double vy = Mother.py / Mother.p0;
122 double vz = Mother.pz / Mother.p0;
123 //SimpleParticle Mo = LorentzBoost(Mother, vx, vy, vz);
124 SimpleParticle Mo = LorentzBoostMomentumOnly(Mother, vx, vy, vz);
125 double ten1 = (Mo.m*Mo.m - m2 * m2 + m1 * m1) / 2. / Mo.m;
126 double tp = sqrt(ten1*ten1 - m1 * m1);
127 double tphi = 2. * xMath::Pi() * RandomGenerators::randgenMT.rand();
128 double cthe = 2. * RandomGenerators::randgenMT.rand() - 1.;
129 double sthe = sqrt(1. - cthe * cthe);
130 ret[0].px = tp * cos(tphi) * sthe;
131 ret[0].py = tp * sin(tphi) * sthe;
132 ret[0].pz = tp * cthe;
133 ret[0].p0 = ten1;
134 ret[1].px = -ret[0].px;
135 ret[1].py = -ret[0].py;
136 ret[1].pz = -ret[0].pz;
137 ret[1].p0 = sqrt(m2*m2 + ret[1].px*ret[1].px + ret[1].py*ret[1].py + ret[1].pz*ret[1].pz);
138
139 double ten2 = ret[1].p0;
140
141 //ret[0] = LorentzBoost(ret[0], -vx, -vy, -vz);
142 //ret[1] = LorentzBoost(ret[1], -vx, -vy, -vz);
143 ret[0] = LorentzBoostMomentumOnly(ret[0], -vx, -vy, -vz);
144 ret[1] = LorentzBoostMomentumOnly(ret[1], -vx, -vy, -vz);
145
146 ret[0].MotherPDGID = Mother.PDGID;
147 ret[1].MotherPDGID = Mother.PDGID;
148
149 ret[0].epoch = Mother.epoch + 1;
150 ret[1].epoch = Mother.epoch + 1;
151
152 for (size_t i = 0; i < ret.size(); ++i)
153 if (ret[i].px != ret[i].px) {
154 std::cerr << "**WARNING** Issue in a two-body decay!" << std::endl;
155 }
156
157#ifdef DEBUGDECAYS
158 if (abs(Mother.p0 - (ret[0].p0 + ret[1].p0)) > 1.e-9) {
159 std::cerr << "Two-body decay energy conservation issue: " << Mother.p0 - (ret[0].p0 + ret[1].p0) << " " << sqrt(vx*vx+vy*vy+vz*vz) << std::endl;
160 std::cerr << Mother.m << " " << ten1 + ten2 << std::endl;
161 }
162#endif
163
164 return ret;
165 }
166
167 std::vector<SimpleParticle> ManyBodyDecay(const SimpleParticle & Mother, std::vector<double> masses, std::vector<long long> pdgs) {
168 std::vector<SimpleParticle> ret(0);
169 if (masses.size() < 1) return ret;
170
171 // If only one daughter listed, assume a radiative decay A -> B + gamma
172 if (masses.size() == 1)
173 {
174 masses.push_back(0.);
175 pdgs.push_back(22);
176 }
177
178 if (masses.size() > 3) {
179 ShuffleDecayProducts(masses, pdgs);
180 }
181
182 SimpleParticle Mother2 = Mother;
183 // Mass validation
184 double tmasssum = 0.;
185 for (size_t i = 0; i < masses.size(); ++i)
186 tmasssum += masses[i];
187
188 // If sum of decay product masses larger than the mother particle mass (happens with zero-width resonances),
189 // adjust the mass and the energy of the mother particle
190 if (Mother2.m < tmasssum) {
191 Mother2.m = tmasssum + 1.e-7;
192 Mother2.p0 = sqrt(Mother2.px * Mother2.px + Mother2.py * Mother2.py + Mother2.pz * Mother2.pz + Mother2.m * Mother2.m);
193 }
194
195 if (masses.size() == 2) return TwoBodyDecay(Mother2, masses[0], pdgs[0], masses[1], pdgs[1]);
196 double tmin = 0.;
197 for (size_t i = 0; i < masses.size() - 1; ++i) tmin += masses[i];
198 double tmax = Mother2.m - masses[masses.size() - 1];
199 double mijk = 0.;
200 if (masses.size() == 3) {
201 mijk = GetRandomThreeBodym12(Mother2.m, masses[0], masses[1], masses[2], 1.01*TernaryThreeBodym12Maximum(Mother2.m, masses[0], masses[1], masses[2]));
202 }
203 else // More than 3 body decay kinematics are only approximate!
204 {
205 mijk = tmin + (tmax - tmin) * RandomGenerators::randgenMT.rand();
206 }
207 std::vector<SimpleParticle> ret1 = TwoBodyDecay(Mother2, mijk, 11111, masses[masses.size() - 1], pdgs[pdgs.size() - 1]);
208 ret.push_back(ret1[1]);
209 masses.resize(masses.size() - 1);
210 pdgs.resize(pdgs.size() - 1);
211 ret1 = ManyBodyDecay(ret1[0], masses, pdgs);
212 for (size_t i = 0; i < ret1.size(); ++i)
213 ret.push_back(ret1[i]);
214
215 for (size_t i = 0; i < ret.size(); ++i) {
216 ret[i].MotherPDGID = Mother.PDGID;
217 ret[i].epoch = Mother.epoch + 1;
218 }
219
220#ifdef DEBUGDECAYS
221 for (int i = 0; i < ret.size(); ++i)
222 if (ret[i].px != ret[i].px) std::cerr << "**WARNING** NaN in NBodyDecay procedure output!" << std::endl;
223#endif
224
225 return ret;
226 }
227
228 void ShuffleDecayProducts(std::vector<double>& masses, std::vector<long long>& pdgs)
229 {
230 if (masses.size() != pdgs.size()) {
231 std::cerr << "**WARNING** ShuffleDecayProducts(): size of masses does not match size of pdgs!" << std::endl;
232 return;
233 }
234 int N = masses.size();
235 for (int i = N - 1; i >= 1; --i) {
236 int j = RandomGenerators::randgenMT.randInt() % (i + 1);
237 std::swap(masses[j], masses[i]);
238 std::swap(pdgs[j], pdgs[i]);
239 }
240 }
241
242 double ParticleDistanceSquared(const SimpleParticle& part1, const SimpleParticle& part2)
243 {
244 double totP0 = part1.p0 + part2.p0;
245 double totPx = part1.px + part2.px;
246 double totPy = part1.py + part2.py;
247 double totPz = part1.pz + part2.pz;
248
249 double vx = totPx / totP0;
250 double vy = totPy / totP0;
251 double vz = totPz / totP0;
252
253 SimpleParticle npart1 = LorentzBoostMomentaAndCoordinates(part1, vx, vy, vz);
254 SimpleParticle npart2 = LorentzBoostMomentaAndCoordinates(part2, vx, vy, vz);
255
256 if (npart1.r0 < npart2.r0)
257 std::swap(npart1, npart2);
258
259 npart2.rx += npart2.px / npart2.p0 * (npart1.r0 - npart2.r0);
260 npart2.ry += npart2.py / npart2.p0 * (npart1.r0 - npart2.r0);
261 npart2.rz += npart2.pz / npart2.p0 * (npart1.r0 - npart2.r0);
262 npart2.r0 = npart1.r0;
263
264 return (npart1.rx - npart2.rx) * (npart1.rx - npart2.rx)
265 + (npart1.ry - npart2.ry) * (npart1.ry - npart2.ry)
266 + (npart1.rz - npart2.rz) * (npart1.rz - npart2.rz);
267 }
268
269 double ComputeDCA(const SimpleParticle& part)
270 {
271 double mult1 = part.rx * part.px + part.ry * part.py + part.rz * part.pz;
272 double mult2 = part.px * part.px + part.py * part.py + part.pz * part.pz;
273 double retx = part.rx - part.px * mult1 / mult2;
274 double rety = part.ry - part.py * mult1 / mult2;
275 double retz = part.rz - part.pz * mult1 / mult2;
276
277 double ret = retx * retx + rety * rety + retz * retz;
278 return sqrt(ret);
279 }
280
281 }
282
283} // namespace thermalfist
Contains functions for Monte Carlo generation of decays.
SimpleParticle LorentzBoostMomentaAndCoordinates(const SimpleParticle &part, double vx, double vy, double vz)
Lorentz boost of the 4-coordinate and 4-momentum of a particle.
double ComputeDCA(const SimpleParticle &part)
Computes the distance of closest approach (DCA) to the origin.
double ThreeBodym12F2(double m12, double M, double m1, double m2, double m3)
Square of the probability density function used in a three-body decay (unnormalized)
std::vector< SimpleParticle > TwoBodyDecay(const SimpleParticle &Mother, double m1, long long pdg1, double m2, long long pdg2)
Samples the decay products of a two-body decay.
double ParticleDistanceSquared(const SimpleParticle &part1, const SimpleParticle &part2)
Return the square of the distance between particles at equal time.
void ShuffleDecayProducts(std::vector< double > &masses, std::vector< long long > &pdgs)
Shuffles the decay products.
SimpleParticle LorentzBoostMomentumOnly(const SimpleParticle &part, double vx, double vy, double vz)
Lorentz boost of the 4-momentum of a particle.
double GetRandomThreeBodym12(double M, double m1, double m2, double m3, double fm12max)
Sample the invariant mass of the leading two daughter particles in a three-body decay.
double TernaryThreeBodym12Maximum(double M, double m1, double m2, double m3)
Determines the maximum of the probability density function used in a three-body decay.
SimpleParticle LorentzBoost(const SimpleParticle &part, double vx, double vy, double vz)
Lorentz boost of the 4-momentum and 4-coordinate of a particle.
int threebodysucc
Used for debugging the succes rate in the rejection sampling of .
std::vector< SimpleParticle > ManyBodyDecay(const SimpleParticle &Mother, std::vector< double > masses, std::vector< long long > pdgs)
Samples the decay products of a many-body decay.
MTRand randgenMT
The Mersenne Twister random number generator.
constexpr double Pi()
Pi constant.
Definition xMath.h:23
The main namespace where all classes and functions of the Thermal-FIST library reside.
Definition CosmicEoS.h:9
Structure holding information about a single particle in the event generator.
double rz
Space-time coordinates.
double p0
Energy (in GeV)
double pz
3-momentum components (in GeV)
int epoch
0 - primary particle, 1 - after decay of primary particles, 2 - after a cascade of two decays and so ...
Contains some extra mathematical functions used in the code.