EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
nBodyPhaseSpaceGen.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file nBodyPhaseSpaceGen.cpp
1 
2 //
3 // Copyright 2010
4 //
5 // This file is part of starlight.
6 //
7 // starlight is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by
9 // the Free Software Foundation, either version 3 of the License, or
10 // (at your option) any later version.
11 //
12 // starlight is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with starlight. If not, see <http://www.gnu.org/licenses/>.
19 //
21 //
22 // File and Version Information:
23 // $Rev:: 211 $: revision of last commit
24 // $Author:: butter $: author of last commit
25 // $Date:: 2015-08-10 03:05:09 +0100 #$: date of last commit
26 //
27 // Description:
28 // see nBodyPhaseSpaceGen.h
29 //
30 //
32 
33 
34 #include <algorithm>
35 
36 #include "nBodyPhaseSpaceGen.h"
37 
38 
39 using namespace std;
40 using namespace starlightConstants;
41 
42 
44  : _n (0),
45  _norm (0),
46  _weight (0),
47  _maxWeightObserved(0),
48  _maxWeight (0),
49  _randy (randy)
50 { }
51 
52 
54 { }
55 
56 
57 // sets decay constants and prepares internal variables
58 bool
59 nBodyPhaseSpaceGen::setDecay(const vector<double>& daughterMasses) // array of daughter particle masses
60 {
61  _n = daughterMasses.size();
62  if (_n < 2) {
63  printWarn << "number of daughters = " << _n << " does not make sense." << endl;
64  return false;
65  }
66  // copy daughter masses
67  _m.clear();
68  _m = daughterMasses;
69  // prepare effective mass vector
70  _M.clear();
71  _M.resize(_n, 0);
72  _M[0] = _m[0];
73  // prepare angle vectors
74  _cosTheta.clear();
75  _cosTheta.resize(_n, 0);
76  _phi.clear();
77  _phi.resize(_n, 0);
78  // calculate daughter mass sums
79  _mSum.clear();
80  _mSum.resize(_n, 0);
81  _mSum[0] = _m[0];
82  for (unsigned int i = 1; i < _n; ++i)
83  _mSum[i] = _mSum[i - 1] + _m[i];
84  // prepare breakup momentum vector
85  _breakupMom.clear();
86  _breakupMom.resize(_n, 0);
87  // prepare vector for daughter Lorentz vectors
88  _daughters.clear();
89  _daughters.resize(_n, lorentzVector(0, 0, 0, 0));
90  // calculate normalization
91  _norm = 1 / (2 * pow(twoPi, 2 * (int)_n - 3) * factorial(_n - 2));
93  return true;
94 }
95 
96 
97 // set decay constants and prepare internal variables
98 bool
99 nBodyPhaseSpaceGen::setDecay(const unsigned int nmbOfDaughters, // number of daughter particles
100  const double* daughterMasses) // array of daughter particle masses
101 {
102  vector <double> m;
103  m.resize(nmbOfDaughters, 0);
104  for (unsigned int i = 0; i < nmbOfDaughters; ++i)
105  m[i] = daughterMasses[i];
106  return setDecay(m);
107 }
108 
109 
110 // generates event with certain n-body mass and momentum and returns event weigth
111 // general purpose function
112 double
113 nBodyPhaseSpaceGen::generateDecay(const lorentzVector& nBody) // Lorentz vector of n-body system in lab frame
114 {
115  const double nBodyMass = nBody.M();
116  if (_n < 2) {
117  printWarn << "number of daughter particles = " << _n << " is smaller than 2. "
118  << "weight is set to 0." << endl;
119  _weight = 0;
120  } else if (nBodyMass < _mSum[_n - 1]) {
121  printWarn << "n-body mass = " << nBodyMass << " is smaller than sum of daughter masses = "
122  << _mSum[_n - 1] << ". weight is set to 0." << endl;
123  _weight = 0;
124  } else {
125  pickMasses(nBodyMass);
126  calcWeight();
127  pickAngles();
128  calcEventKinematics(nBody);
129  }
130  return _weight;
131 }
132 
133 
134 // generates full event with certain n-body mass and momentum only, when event is accepted (return value = true)
135 // this function is more efficient, if only weighted evens are needed
136 bool
137 nBodyPhaseSpaceGen::generateDecayAccepted(const lorentzVector& nBody, // Lorentz vector of n-body system in lab frame
138  const double maxWeight) // if positive, given value is used as maximum weight, otherwise _maxWeight
139 {
140  const double nBodyMass = nBody.M();
141  if (_n < 2) {
142  printWarn << "number of daughter particles = " << _n << " is smaller than 2. "
143  << "no event generated." << endl;
144  return false;
145  } else if (nBodyMass < _mSum[_n - 1]) {
146  printWarn << "n-body mass = " << nBodyMass << " is smaller than sum of daughter masses = "
147  << _mSum[_n - 1] << ". no event generated." << endl;
148  return false;
149  }
150  pickMasses(nBodyMass);
151  calcWeight();
152  if (!eventAccepted(maxWeight))
153  return false;
154  pickAngles();
155  calcEventKinematics(nBody);
156  return true;
157 }
158 
159 
160 // randomly choses the (n - 2) effective masses of the respective (i + 1)-body systems
161 void
162 nBodyPhaseSpaceGen::pickMasses(const double nBodyMass) // total energy of the system in its RF
163 {
164  _M[_n - 1] = nBodyMass;
165  // create vector of sorted random values
166  vector<double> r(_n - 2, 0); // (n - 2) values needed for 2- through (n - 1)-body systems
167  for (unsigned int i = 0; i < (_n - 2); ++i)
168  r[i] = random();
169  sort(r.begin(), r.end());
170  // set effective masses of (intermediate) two-body decays
171  const double massInterval = nBodyMass - _mSum[_n - 1]; // kinematically allowed mass interval
172  for (unsigned int i = 1; i < (_n - 1); ++i) // loop over intermediate 2- to (n - 1)-bodies
173  _M[i] = _mSum[i] + r[i - 1] * massInterval; // _mSum[i] is minimum effective mass
174 }
175 
176 
177 // computes event weight (= integrand value) and breakup momenta
178 // uses vector of intermediate two-body masses prepared by pickMasses()
179 double
181 {
182  for (unsigned int i = 1; i < _n; ++i) // loop over 2- to n-bodies
183  _breakupMom[i] = breakupMomentum(_M[i], _M[i - 1], _m[i]);
184  double momProd = 1; // product of breakup momenta
185  for (unsigned int i = 1; i < _n; ++i) // loop over 2- to n-bodies
186  momProd *= _breakupMom[i];
187  const double massInterval = _M[_n - 1] - _mSum[_n - 1]; // kinematically allowed mass interval
188  _weight = _norm * pow(massInterval, (int)_n - 2) * momProd / _M[_n - 1];
191  if (std::isnan(_weight))
192  printWarn << "weight = " << _weight << endl;
193  return _weight;
194 }
195 
196 
197 // calculates complete event from the effective masses of the (i + 1)-body
198 // systems, the Lorentz vector of the decaying system, and the decay angles
199 // uses the break-up momenta calculated by calcWeight()
200 void
201 nBodyPhaseSpaceGen::calcEventKinematics(const lorentzVector& nBody) // Lorentz vector of n-body system in lab frame
202 {
203  // build event starting in n-body RF going down to 2-body RF
204  // is more efficicient than Raubold-Lynch method, since it requitres only one rotation and boost per daughter
205  lorentzVector P = nBody; // Lorentz of (i + 1)-body system in lab frame
206  for (unsigned int i = _n - 1; i >= 1; --i) { // loop from n-body down to 2-body
207  // construct Lorentz vector of daughter _m[i] in (i + 1)-body RF
208  const double sinTheta = sqrt(1 - _cosTheta[i] * _cosTheta[i]);
209  const double pT = _breakupMom[i] * sinTheta;
211  daughter.SetPxPyPzE(pT * cos(_phi[i]),
212  pT * sin(_phi[i]),
213  _breakupMom[i] * _cosTheta[i],
214  sqrt(_m[i] * _m[i] + _breakupMom[i] * _breakupMom[i]));
215  // boost daughter into lab frame
216  daughter.Boost(P.BoostVector());
217  // calculate Lorentz vector of i-body system in lab frame
218  P -= daughter;
219  }
220  // set last daughter
221  _daughters[0] = P;
222 }
223 
224 
225 // calculates maximum weight for given n-body mass
226 double
227 nBodyPhaseSpaceGen::estimateMaxWeight(const double nBodyMass, // sic!
228  const unsigned int nmbOfIterations) // number of generated events
229 {
230  double maxWeight = 0;
231  for (unsigned int i = 0; i < nmbOfIterations; ++i) {
232  pickMasses(nBodyMass);
233  calcWeight();
234  maxWeight = max(_weight, maxWeight);
235  }
236  return maxWeight;
237 }
238 
239 
240 ostream&
241 nBodyPhaseSpaceGen::print(ostream& out) const
242 {
243  out << "nBodyPhaseSpaceGen parameters:" << endl
244  << " number of daughter particles ............... " << _n << endl
245  << " masses of the daughter particles ........... " << _m << endl
246  << " sums of daughter particle masses ........... " << _mSum << endl
247  << " effective masses of (i + 1)-body systems ... " << _M << endl
248  << " cos(polar angle) in (i + 1)-body systems ... " << _cosTheta << endl
249  << " azimuth in (i + 1)-body systems ............ " << _phi << endl
250  << " breakup momenta in (i + 1)-body systems .... " << _breakupMom << endl
251  << " normalization value ........................ " << _norm << endl
252  << " weight of generated event .................. " << _weight << endl
253  << " maximum weight used in hit-miss MC ......... " << _maxWeight << endl
254  << " maximum weight since instantiation ......... " << _maxWeightObserved << endl
255  << " daughter four-momenta:" << endl;
256  for (unsigned int i = 0; i < _n; ++i)
257  out << " daughter " << i << ": " << _daughters[i] << endl;
258  return out;
259 }