StRoot  1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
HadronWidths.cc
1 // HadronWidths.cc is a part of the PYTHIA event generator.
2 // Copyright (C) 2020 Torbjorn Sjostrand.
3 // PYTHIA is licenced under the GNU GPL v2 or later, see COPYING for details.
4 // Please respect the MCnet Guidelines, see GUIDELINES for details.
5 
6 // Function definitions (not found in the header) for the HadronWidths class.
7 
8 #include "Pythia8/HadronWidths.h"
9 
10 namespace Pythia8 {
11 
12 //--------------------------------------------------------------------------
13 
14 // Static methods for reading xml files.
15 
16 static string attributeValue(string line, string attribute) {
17  if (line.find(attribute) == string::npos) return "";
18  int iBegAttri = line.find(attribute);
19  int iBegQuote = line.find("\"", iBegAttri + 1);
20  int iEndQuote = line.find("\"", iBegQuote + 1);
21  return line.substr(iBegQuote + 1, iEndQuote - iBegQuote - 1);
22 }
23 
24 static int intAttributeValue(string line, string attribute) {
25  string valString = attributeValue(line, attribute);
26  if (valString == "") return 0;
27  istringstream valStream(valString);
28  int intVal;
29  valStream >> intVal;
30  return intVal;
31 }
32 
33 static double doubleAttributeValue(string line, string attribute) {
34  string valString = attributeValue(line, attribute);
35  if (valString == "") return 0.;
36  istringstream valStream(valString);
37  double doubleVal;
38  valStream >> doubleVal;
39  return doubleVal;
40 }
41 
42 static void completeTag(istream& stream, string& line) {
43  while (line.find(">") == string::npos) {
44  string addLine;
45  if (!getline(stream, addLine)) break;
46  line += " " + addLine;
47  }
48 }
49 
50 //==========================================================================
51 
52 // The HadronWidths class.
53 
54 //--------------------------------------------------------------------------
55 
56 // Initialize.
57 
58 bool HadronWidths::init(string path) {
59 
60  ifstream stream(path);
61  if (!stream.is_open()) {
62  infoPtr->errorMsg( "Error in HadronWidths::init: "
63  "unable to open file");
64  return false;
65  }
66 
67  return init(stream);
68 }
69 
70 //--------------------------------------------------------------------------
71 
72 // Initialize.
73 
74 bool HadronWidths::init(istream& stream) {
75 
76  string line;
77 
78  while (getline(stream, line)) {
79 
80  string word1;
81  if (!(istringstream(line) >> word1))
82  continue;
83 
84  if (word1 == "<width") {
85  completeTag(stream, line);
86 
87  int id = intAttributeValue(line, "id");
88 
89  if (entries.find(id) != entries.end()) {
90  infoPtr->errorMsg( "Error in HadronWidths::readXML: "
91  "resonance is defined more than once",
92  std::to_string(id));
93  continue;
94  }
95 
96  double left = doubleAttributeValue(line, "left");
97  double right = doubleAttributeValue(line, "right");
98 
99  istringstream dataStr(attributeValue(line, "data"));
100  vector<double> data;
101  double currentData;
102  while (dataStr >> currentData)
103  data.push_back(currentData);
104 
105  // Insert resonance in entries
106  LinearInterpolator widths(left, right, data);
107  entries.emplace(id, HadronWidthEntry{ widths, {} });
108 
109  // Insert resonance in signature map
110  int signature = getSignature(particleDataPtr->isBaryon(id),
111  particleDataPtr->chargeType(id));
112 
113  auto iter = signatureToParticles.find(signature);
114  if (iter == signatureToParticles.end())
115  // If signature has not been used yet, insert a new vector into the map
116  signatureToParticles.emplace(signature, vector<int> { id });
117  else
118  // If signature has been used already, add id to the existing vector
119  iter->second.push_back(id);
120  }
121  else if (word1 == "<partialWidth") {
122  completeTag(stream, line);
123 
124  int id = intAttributeValue(line, "id");
125 
126  auto entryIter = entries.find(id);
127  if (entryIter == entries.end()) {
128  infoPtr->errorMsg( "Error in HadronWidths::readXML: "
129  "got partial width for a particle with undefined total width",
130  std::to_string(id));
131  continue;
132  }
133 
134  int lType = intAttributeValue(line, "lType");
135 
136  istringstream productStr(attributeValue(line, "products"));
137  int prod1, prod2;
138  productStr >> prod1;
139  productStr >> prod2;
140 
141  istringstream dataStr(attributeValue(line, "data"));
142  vector<double> data;
143  double currentData;
144  while (dataStr >> currentData)
145  data.push_back(currentData);
146 
147  HadronWidthEntry& entry = entryIter->second;
148  LinearInterpolator widths(entry.width.left(), entry.width.right(), data);
149 
150  // Generate key to ensure canonical ordering of decay products.
151  pair<int, int> key = getKey(id, prod1, prod2);
152  double mThreshold = particleDataPtr->mMin(key.first)
153  + particleDataPtr->mMin(key.second);
154 
155  // Insert new decay channel.
156  entry.decayChannels.emplace(key, ResonanceDecayChannel {
157  widths, key.first, key.second, lType, mThreshold });
158  }
159  }
160 
161  // Done.
162  return true;
163 }
164 
165 //--------------------------------------------------------------------------
166 
167 // Check whether input data is valid and matches particle data.
168 
169 bool HadronWidths::check() {
170 
171  // Check that all resonance entries make sense.
172  for (auto& entryPair : entries) {
173  int id = entryPair.first;
174  HadronWidthEntry& entry = entryPair.second;
175 
176  // Check that entry id actually corresponds to a particle.
177  if (!particleDataPtr->isParticle(id)) {
178  infoPtr->errorMsg("Error in HadronWidths::check: "
179  "resonance is not a particle", std::to_string(id));
180  return false;
181  }
182 
183  // Check that entry id is positive (antiparticles are handled by symmetry).
184  if (id < 0) {
185  infoPtr->errorMsg("Error in HadronWidths::check: "
186  "resonance is an antiparticle", std::to_string(id));
187  return false;
188  }
189 
190  // Check that entry id is hadron.
191  if (!particleDataPtr->isHadron(id)) {
192  infoPtr->errorMsg("Error in HadronWidths::check: "
193  "resonance is not a hadron", std::to_string(id));
194  return false;
195  }
196 
197  // Check that mass boundaries are same in particle entry and widths entry.
198  if (particleDataPtr->mMin(id) < entry.width.left()) {
199  infoPtr->errorMsg("Warning in HadronWidths::check: "
200  "inconsistent lower mass bound", std::to_string(id));
201  }
202  if (particleDataPtr->mMax(id) > entry.width.right()) {
203  infoPtr->errorMsg("Warning in HadronWidths::check: "
204  "inconsistent upper mass bound", std::to_string(id));
205  }
206 
207  // Check that all decay channels make sense.
208  for (auto channelPair : entry.decayChannels) {
209  ResonanceDecayChannel& channel = channelPair.second;
210  int idA = channel.prodA, idB = channel.prodB;
211  string channelStr = std::to_string(id) + " --> "
212  + std::to_string(idA) + " + " + std::to_string(idB);
213 
214  // Check that decay product ids actually correspond to particles.
215  for (int idProd : { idA, idB })
216  if (!particleDataPtr->isParticle(idProd)) {
217  infoPtr->errorMsg("Error in HadronWidths::check: "
218  "decay product is not a particle", std::to_string(idProd));
219  return false;
220  }
221  // Check that lType makes sense.
222  if (channel.lType <= 0) {
223  infoPtr->errorMsg("Error in HadronWidths::check: "
224  "decay channel does not specify a valid lType", channelStr);
225  return false;
226  }
227 
228  // Check that decay conserves charge.
229  if (particleDataPtr->chargeType(idA) + particleDataPtr->chargeType(idB)
230  != particleDataPtr->chargeType(id)) {
231  infoPtr->errorMsg("Error in HadronWidths::check: "
232  "decay does not conserve charge", channelStr);
233  return false;
234  }
235  }
236  }
237 
238  for (auto& entry : *particleDataPtr) {
239  if (entry.second.varWidth() && !hasData(entry.first)) {
240  infoPtr->errorMsg("Warning in HadronWidths::check: "
241  "particle uses mass dependent width, but width is not defined",
242  std::to_string(entry.first));
243  }
244  }
245 
246  return true;
247 }
248 
249 //--------------------------------------------------------------------------
250 
251 // Gets key for the decay and flips idR if necessary
252 
253 pair<int, int> HadronWidths::getKey(int& idR, int idA, int idB) const {
254 
255  if (idR < 0) {
256  idR = -idR;
257  idA = particleDataPtr->antiId(idA);
258  idB = particleDataPtr->antiId(idB);
259  }
260 
261  if (abs(idA) < abs(idB))
262  return { idB, idA };
263  else
264  return { idA, idB };
265 }
266 
267 //--------------------------------------------------------------------------
268 
269 // Get signature of system based on total baryon number and electric charge.
270 
271 int HadronWidths::getSignature(int baryonNumber, int charge) const {
272  return 100 * baryonNumber
273  + 10 * ((charge >= 0) ? charge : (10 + charge));
274 }
275 
276 //--------------------------------------------------------------------------
277 
278 // Get a list of all implemented resonances.
279 
280 vector<int> HadronWidths::getResonances() const {
281  vector<int> resonances;
282  for (auto& p : entries)
283  resonances.push_back(p.first);
284  return resonances;
285 }
286 //--------------------------------------------------------------------------
287 
288 // Get whether the specified incoming particles can form a resonance.
289 
290 bool HadronWidths::hasResonances(int idA, int idB) const {
291 
292  ParticleDataEntry* entryA = particleDataPtr->findParticle(idA);
293  ParticleDataEntry* entryB = particleDataPtr->findParticle(idB);
294  if (!entryA || !entryB) {
295  infoPtr->errorMsg("Error in HadronWidths::possibleResonances: "
296  "invalid input particle ids");
297  return false;
298  }
299 
300  // Get signature for system and look only for resonances that matches it.
301  int baryonNumber = entryA->isBaryon() + entryB->isBaryon();
302  int charge = entryA->chargeType(idA) + entryB->chargeType(idB);
303  int signature = getSignature(baryonNumber, charge);
304  auto iter = signatureToParticles.find(signature);
305  if (iter == signatureToParticles.end())
306  return false;
307 
308  // For resonances that matches signature, check that decay channel exists.
309  for (int res : iter->second)
310  if (canDecay(res, idA, idB))
311  return true;
312 
313  // No resonances found.
314  return false;
315 }
316 
317 //--------------------------------------------------------------------------
318 
319 // Get resonances that can be formed by the specified incoming particles.
320 
321 vector<int> HadronWidths::possibleResonances(int idA, int idB) const {
322 
323  vector<int> resonances;
324  ParticleDataEntry* entryA = particleDataPtr->findParticle(idA);
325  ParticleDataEntry* entryB = particleDataPtr->findParticle(idB);
326  if (!entryA || !entryB) {
327  infoPtr->errorMsg("Error in HadronWidths::possibleResonances: "
328  "invalid input particle ids");
329  return resonances;
330  }
331 
332  // Get signature for system and look only for resonances that matches it.
333  int baryonNumber = entryA->isBaryon() + entryB->isBaryon();
334  int charge = entryA->chargeType(idA) + entryB->chargeType(idB);
335  int signature = getSignature(baryonNumber, charge);
336  auto iter = signatureToParticles.find(signature);
337  if (iter == signatureToParticles.end())
338  return vector<int>();
339 
340  // For resonances that matches signature, check that decay channel exists.
341  for (int res : iter->second)
342  if (canDecay(res, idA, idB))
343  resonances.push_back(res);
344 
345  // For pi0pi0 and pi+pi-, add f0(500) explicitly.
346  if ( (idA == 111 && idB == 111)
347  || (abs(idA) == 211 && abs(idB) == 211 && idA * idB < 0) )
348  resonances.push_back(9000221);
349 
350  // Done.
351  return resonances;
352 }
353 
354 //--------------------------------------------------------------------------
355 
356 // Get whether the resonance can decay into the specified products.
357 
358 bool HadronWidths::canDecay(int idR, int idA, int idB) const {
359 
360  auto entryIter = entries.find(idR);
361  if (entryIter == entries.end())
362  return false;
363 
364  pair<int, int> key = getKey(idR, idA, idB);
365  auto channelIter = entryIter->second.decayChannels.find(key);
366  return channelIter != entryIter->second.decayChannels.end();
367 }
368 
369 //--------------------------------------------------------------------------
370 
371 // Get the total width of the specified particle at the specified mass.
372 
373 double HadronWidths::width(int id, double m) const {
374  auto iter = entries.find(abs(id));
375  return (iter != entries.end()) ? iter->second.width(m)
376  : particleDataPtr->mWidth(id);
377 }
378 
379 //--------------------------------------------------------------------------
380 
381 // Get the partial width for the specified decay channel of the particle.
382 
383 double HadronWidths::partialWidth(int idR, int idA, int idB, double m) const {
384 
385  auto entryIter = entries.find(idR);
386  if (entryIter == entries.end())
387  return 0.;
388 
389  pair<int, int> key = getKey(idR, idA, idB);
390  auto channelIter = entryIter->second.decayChannels.find(key);
391  if (channelIter == entryIter->second.decayChannels.end())
392  return 0.;
393 
394  return (m <= channelIter->second.mThreshold) ? 0.
395  : channelIter->second.partialWidth(m);
396 }
397 
398 //--------------------------------------------------------------------------
399 
400 // Get the branching ratio for the specified decay channel of the particle.
401 
402 double HadronWidths::br(int idR, int idA, int idB, double m) const {
403 
404  auto entryIter = entries.find(idR);
405  if (entryIter == entries.end())
406  return 0.;
407 
408  pair<int, int> key = getKey(idR, idA, idB);
409  auto channelIter = entryIter->second.decayChannels.find(key);
410  if (channelIter == entryIter->second.decayChannels.end())
411  return 0.;
412 
413  double widthNow = entryIter->second.width(m);
414  if (widthNow == 0.)
415  return 0.;
416  else
417  return (m <= channelIter->second.mThreshold) ? 0.
418  : channelIter->second.partialWidth(m) / widthNow;
419 }
420 
421 //--------------------------------------------------------------------------
422 
423 // Get the mass distribution density for the particle at the specified mass.
424 
425 double HadronWidths::mDistr(int id, double m) const {
426  auto iter = entries.find(abs(id));
427  double w = (iter == entries.end()) ? particleDataPtr->mWidth(id)
428  : iter->second.width(m);
429  double m0 = particleDataPtr->m0(id);
430  return 0.5 / M_PI * w / (pow2(m - m0) + 0.25 * w * w);
431 }
432 
433 //--------------------------------------------------------------------------
434 
435 // Pick a decay channel for the specified particle, together with phase
436 // space configuration. Returns whether successful.
437 
438 bool HadronWidths::pickDecay(int idDec, double m, int& idAOut, int& idBOut,
439  double& mAOut, double& mBOut) {
440 
441  // Find table entry for decaying particle.
442  bool isAnti = (idDec < 0);
443  if (isAnti) idDec = -idDec;
444  auto entriesIter = entries.find(idDec);
445  if (entriesIter == entries.end()) {
446  infoPtr->errorMsg("Error in HadronWidths::pickDecay: "
447  "particle not found", std::to_string(idDec));
448  return false;
449  }
450  HadronWidthEntry& entry = entriesIter->second;
451 
452  // Pick decay channel.
453  vector<pair<int, int>> prodsList;
454  vector<double> sigmas;
455  bool gotAny = false;
456  for (auto& channel : entry.decayChannels) {
457  if (m <= channel.second.mThreshold)
458  continue;
459  double sigma = channel.second.partialWidth(m);
460  if (sigma > 0.) {
461  gotAny = true;
462  prodsList.push_back(channel.first);
463  sigmas.push_back(sigma);
464  }
465  }
466  if (!gotAny) {
467  infoPtr->errorMsg("Error in HadronWidths::pickDecay: "
468  "no channels have positive widths",
469  "for " + to_string(idDec) + " @ " + to_string(m) + " GeV");
470  return false;
471  }
472 
473  // Select decay products. Check spin type of decay.
474  pair<int, int> prods = prodsList[rndmPtr->pick(sigmas)];
475  int idA = prods.first;
476  int idB = prods.second;
477  int lType = entry.decayChannels.at(prods).lType;
478 
479  // Select masses of decay products.
480  double mA, mB;
481  if (!pickMasses(idA, idB, m, mA, mB, lType)) {
482  infoPtr->errorMsg("Error in HadronWidths::pickDecay: failed to pick "
483  "masses", "for " + to_string(idDec) + " --> " + to_string(idA)
484  + " + " + to_string(idB) + " @ " + to_string(m));
485  return false;
486  }
487 
488  // Done.
489  idAOut = isAnti ? particleDataPtr->antiId(idA) : idA;
490  idBOut = isAnti ? particleDataPtr->antiId(idB) : idB;
491  mAOut = mA;
492  mBOut = mB;
493  return true;
494 
495 }
496 
497 //--------------------------------------------------------------------------
498 
499 // Constants used when sampling masses.
500 static constexpr int MAXLOOP = 100;
501 static constexpr double MINWIDTH = 0.001;
502 static constexpr double MAXWIDTHGROWTH = 2.;
503 
504 //--------------------------------------------------------------------------
505 
506 // Pick a pair of masses given pair invariant mass and angular momentum.
507 
508 bool HadronWidths::pickMasses(int idA, int idB, double eCM,
509  double& mAOut, double& mBOut, int lType) {
510 
511  // Minimal masses must be a possible choice.
512  double mAMin = particleDataPtr->mMin(idA);
513  double mBMin = particleDataPtr->mMin(idB);
514  if (mAMin + mBMin >= eCM) {
515  infoPtr->errorMsg("Error in HadronWidths::pickMasses: "
516  "energy is smaller than minimum masses");
517  return false;
518  }
519 
520  if (lType <= 0) {
521  infoPtr->errorMsg("Error in HadronWidths::pickMasses: "
522  "invalid angular momentum", "2l+1 = " + to_string(lType));
523  return false;
524  }
525 
526  // Done if none of the daughters have a width.
527  double mAFix = particleDataPtr->m0(idA);
528  double gammaAFix = particleDataPtr->mWidth(idA);
529  bool hasFixWidthA = (gammaAFix > MINWIDTH);
530  double mBFix = particleDataPtr->m0(idB);
531  double gammaBFix = particleDataPtr->mWidth(idB);
532  bool hasFixWidthB = (gammaBFix > MINWIDTH);
533  mAOut = mAFix;
534  mBOut = mBFix;
535  if (!hasFixWidthA && !hasFixWidthB) return true;
536 
537  // Get width entries for particles with mass-depedent widths.
538  bool hasVarWidthA = hasData(idA) && particleDataPtr->varWidth(idA);
539  bool hasWidthA = hasFixWidthA || hasVarWidthA;
540  HadronWidthEntry* entryA = nullptr;
541  if (hasVarWidthA) {
542  auto iterA = entries.find( abs(idA) );
543  if (iterA == entries.end()) {
544  infoPtr->errorMsg("Error in HadronWidths::pickMasses: "
545  "mass distribution for particle is not defined", std::to_string(idA));
546  return false;
547  }
548  entryA = &iterA->second;
549  }
550  bool hasVarWidthB = hasData(idB) && particleDataPtr->varWidth(idB);
551  bool hasWidthB = hasFixWidthB || hasVarWidthB;
552  HadronWidthEntry* entryB = nullptr;
553  if (hasVarWidthB) {
554  auto iterB = entries.find( abs(idB) );
555  if (iterB == entries.end()) {
556  infoPtr->errorMsg("Error in HadronWidths::pickMasses: "
557  "mass distribution for particle is not defined", std::to_string(idB));
558  return false;
559  }
560  entryB = &iterB->second;
561  }
562 
563  // Parameters for mass selection.
564  double mAMax = min(particleDataPtr->mMax(idA), eCM - mBMin);
565  if (hasVarWidthA) gammaAFix = entryA->width(mAFix);
566  double bwAMin = (hasWidthA) ? atan(2. * (mAMin - mAFix) / gammaAFix) : 0.;
567  double bwAMax = (hasWidthA) ? atan(2. * (mAMax - mAFix) / gammaAFix) : 0.;
568  double mBMax = min(particleDataPtr->mMax(idB), eCM - mAMin);
569  if (hasVarWidthB) gammaBFix = entryB->width(mBFix);
570  double bwBMin = (hasWidthB) ? atan(2. * (mBMin - mBFix) / gammaBFix) : 0.;
571  double bwBMax = (hasWidthB) ? atan(2. * (mBMax - mBFix) / gammaBFix) : 0.;
572  double p2Max = (eCM*eCM - pow2(mAMin + mBMin))
573  * (eCM*eCM - pow2(mAMin - mBMin));
574 
575  // Loop over attempts to pick the two masses simultaneously.
576  double wtTot, gammaAVar, gammaBVar, bwAFix, bwBFix, bwAVar, bwBVar;
577  for (int i = 0; i < MAXLOOP; ++i) {
578  wtTot = 1.;
579 
580  // Simplify handling if full procedure does not seem to work.
581  if (2 * i > MAXLOOP) {
582  hasVarWidthA = false;
583  hasVarWidthB = false;
584  }
585  if (4 * i > 3 * MAXLOOP) lType = 0;
586 
587  // Initially pick according to simple Breit-Wigner.
588  if (hasWidthA) mAOut = mAFix + 0.5 * gammaAFix * tan(bwAMin
589  + rndmPtr->flat() * (bwAMax - bwAMin));
590  if (hasWidthB) mBOut = mBFix + 0.5 * gammaBFix * tan(bwBMin
591  + rndmPtr->flat() * (bwBMax - bwBMin));
592 
593  // Correction given by BW(Gamma_now)/BW(Gamma_fix) for variable width.
594  // Note: width not allowed to explode at large masses.
595  if (hasVarWidthA) {
596  gammaAVar = min(entryA->width(mAOut), MAXWIDTHGROWTH * gammaAFix);
597  bwAVar = gammaAVar / (pow2( mAOut - mAFix) + 0.25 * pow2(gammaAVar));
598  bwAFix = gammaAFix / (pow2( mAOut - mAFix) + 0.25 * pow2(gammaAFix));
599  wtTot *= bwAVar / (bwAFix * MAXWIDTHGROWTH);
600  }
601  if (hasVarWidthB) {
602  gammaBVar = min(entryB->width(mBOut), MAXWIDTHGROWTH * gammaBFix);
603  bwBVar = gammaBVar / (pow2( mBOut - mBFix) + 0.25 * pow2(gammaBVar));
604  bwBFix = gammaBFix / (pow2( mBOut - mBFix) + 0.25 * pow2(gammaBFix));
605  wtTot *= bwBVar / (bwBFix * MAXWIDTHGROWTH);
606  }
607 
608  // Weight by (p/p_max)^lType.
609  if (mAOut + mBOut >= eCM) continue;
610  double p2Ratio = (eCM*eCM - pow2(mAOut + mBOut))
611  * (eCM*eCM - pow2(mAOut - mBOut)) / p2Max;
612  if (lType > 0) wtTot *= pow(p2Ratio, 0.5 * lType);
613  if (wtTot > rndmPtr->flat()) {
614  // Give warning message only for more severe cases
615  if (4 * i > 3 * MAXLOOP) infoPtr->errorMsg("Warning in HadronWidths::"
616  "pickMasses: angular momentum and running widths not used");
617  return true;
618  }
619  }
620 
621  // Last resort: pick masses within limits known to work, without weight.
622  infoPtr->errorMsg("Warning in HadronWidths::pickMasses: "
623  "using last-resort simplified description");
624  double mSpanNorm = (eCM - mAMin - mBMin) / (gammaAFix + gammaBFix);
625  mAOut = mAMin + rndmPtr->flat() * mSpanNorm * gammaAFix;
626  mBOut = mBMin + rndmPtr->flat() * mSpanNorm * gammaBFix;
627 
628  // Done.
629  return true;
630 
631 }
632 
633 //--------------------------------------------------------------------------
634 
635 // Calculate the total width of the particle without using interpolation.
636 
637 double HadronWidths::widthCalc(int id, double m) const {
638 
639  // Get particle entry.
640  ParticleDataEntry* entry = particleDataPtr->findParticle(id);
641  if (entry == nullptr) {
642  infoPtr->errorMsg("Error in HadronWidths::widthCalc: "
643  "particle not found", to_string(id));
644  return 0.;
645  }
646 
647  // Sum contributions from all channels.
648  double w = 0.;
649  for (int iChan = 0; iChan < entry->sizeChannels(); ++iChan)
650  w += widthCalc(id, entry->channel(iChan), m);
651  return w;
652 }
653 
654 //--------------------------------------------------------------------------
655 
656 // Calculate partial width of the particle without using interpolation.
657 
658 double HadronWidths::widthCalc(int id, int prodA, int prodB, double m) const {
659 
660  // Find particle entry.
661  pair<int, int> key = getKey(id, prodA, prodB);
662  ParticleDataEntry* entry = particleDataPtr->findParticle(id);
663  if (entry == nullptr)
664  return 0.;
665 
666  // Search for the matching decay channel.
667  for (int iChan = 0; iChan < entry->sizeChannels(); ++iChan) {
668  DecayChannel& channel = entry->channel(iChan);
669  if (channel.multiplicity() > 2)
670  continue;
671  if ( (channel.product(0) == key.first && channel.product(1) == key.second)
672  || (channel.product(1) == key.first && channel.product(0) == key.second))
673  return widthCalc(id, channel, m);
674  }
675 
676  // Decay channel not found.
677  infoPtr->errorMsg("Error in HadronWidths::widthCalc: "
678  "decay channel not found",
679  to_string(id) + " --> " + to_string(prodA) + " " + to_string(prodB));
680  return 0.;
681 }
682 
683 //--------------------------------------------------------------------------
684 
685 // Calculate partial width of the particle without using interpolation.
686 
687 double HadronWidths::widthCalc(int id, Pythia8::DecayChannel& channel,
688  double m) const {
689 
690  // Get particle entry.
691  ParticleDataEntry* entry = particleDataPtr->findParticle(id);
692  if (entry == nullptr) {
693  infoPtr->errorMsg("Error in HadronWidths::widthCalc: "
694  "particle not found", to_string(id));
695  return 0.;
696  }
697 
698  // Store nominal mass and partial width.
699  double m0 = entry->m0(), gamma0 = channel.bRatio() * entry->mWidth();
700 
701  // Only two-body decays can have mass-dependent width.
702  if (channel.multiplicity() != 2)
703  return channel.bRatio();
704  auto& prodA = *particleDataPtr->findParticle(channel.product(0));
705  auto& prodB = *particleDataPtr->findParticle(channel.product(1));
706 
707  if (m < prodA.mMin() + prodB.mMin())
708  return 0.;
709 
710  // Get two-body angular momentum.
711  int lType;
712  if (channel.meMode() >= 3 && channel.meMode() <= 7)
713  lType = 2 * (channel.meMode() - 3) + 1;
714  else if (channel.meMode() == 2)
715  lType = 3;
716  else
717  lType = 1;
718 
719  // Calculate phase space at the specified mass.
720  double pM = psSize(m, prodA, prodB, lType);
721  if (pM == 0.)
722  return 0.;
723  double pMS = psSize(m, prodA, prodB, lType - 1);
724  if (pMS == 0.)
725  return 0.;
726 
727  // Calculate phase space at on-shell mass.
728  double pM0 = psSize(m0, prodA, prodB, lType);
729  double pM0S = psSize(m0, prodA, prodB, lType - 1);
730  if (pM0 <= 0 || pM0S <= 0) {
731  infoPtr->errorMsg("Error in HadronWidths::widthCalc: "
732  "on-shell decay is not possible",
733  to_string(id) + " --> " + to_string(prodA.id())
734  + " " + to_string(prodB.id()));
735  return NAN;
736  }
737 
738  // Return mass-dependent partial width.
739  return gamma0 * (m0 / m) * (pM / pM0) * 1.2 / (1. + 0.2 * pMS / pM0S);
740 }
741 
742 //--------------------------------------------------------------------------
743 
744 // Regenerate parameterization for the specified particle.
745 
746 bool HadronWidths::parameterize(int id, int precision) {
747 
748  // Get particle entry and validate input.
749  ParticleDataEntry* entry = particleDataPtr->findParticle(id);
750 
751  if (entry == nullptr) {
752  infoPtr->errorMsg("Error in HadronWidths::parameterize: "
753  "particle does not exist", to_string(id));
754  return false;
755  }
756  if (precision <= 1) {
757  infoPtr->errorMsg("Error in HadronWidths::parameterize: "
758  "precision must be at least 2");
759  return false;
760  }
761  if (entry->mMin() >= entry->mMax()) {
762  infoPtr->errorMsg("Error in HadronWidths::parameterize: "
763  "particle has fixed mass", to_string(id));
764  return false;
765  }
766 
767  if (!entry->varWidth())
768  infoPtr->errorMsg("Warning in HadronWidths::parameterize: "
769  "particle does not have mass-dependent width", to_string(id));
770 
771  map<pair<int, int>, ResonanceDecayChannel> partialWidths;
772  vector<double> totalWidthData(precision);
773 
774  double mMin = entry->mMin(), mMax = entry->mMax();
775  double dm = (mMax - mMin) / (precision - 1);
776 
777  // Parameterize all channels.
778  for (int iChan = 0; iChan < entry->sizeChannels(); ++iChan) {
779 
780  // Mass-dependent width is not defined for multibody channels.
781  DecayChannel& channel = entry->channel(iChan);
782  if (channel.multiplicity() != 2)
783  continue;
784 
785  // Create key to put decay products in canonical order.
786  pair<int, int> key = getKey(id, channel.product(0), channel.product(1));
787  int prodA = key.first, prodB = key.second;
788 
789  // Calculate widths at regular mass intervals.
790  vector<double> widthData(precision);
791  for (int j = 0; j < precision; ++j) {
792  double m = mMin + j * dm;
793  widthData[j] = widthCalc(entry->id(), channel, m);
794  totalWidthData[j] += widthData[j];
795  }
796 
797  // Get two-body angular momentum.
798  int lType;
799  if (channel.meMode() >= 3 && channel.meMode() <= 7)
800  lType = 2 * (channel.meMode() - 3) + 1;
801  else if (channel.meMode() == 2)
802  lType = 3;
803  else
804  lType = 1;
805 
806  // Add new ResonanceDecayChannel to map.
807  partialWidths.emplace(make_pair(prodA, prodB), ResonanceDecayChannel {
808  LinearInterpolator(mMin, mMax, widthData),
809  prodA, prodB, lType,
810  max(mMin, particleDataPtr->mMin(prodA) + particleDataPtr->mMin(prodB))
811  });
812  }
813 
814  // Create new or update existing HadronWidthEntry.
815  HadronWidthEntry newEntry {
816  LinearInterpolator(mMin, mMax, totalWidthData),
817  partialWidths
818  };
819  auto iter = entries.find(id);
820  if (iter == entries.end())
821  entries.emplace(id, newEntry);
822  else
823  entries[id] = newEntry;
824 
825  // Done.
826  return true;
827 }
828 
829 //--------------------------------------------------------------------------
830 
831 // Helper function to calculate CM momentum.
832 
833 static double pCMS(double eCM, double mA, double mB) {
834  if (eCM <= mA + mB) return 0;
835  double sCM = eCM * eCM;
836  return sqrt((sCM - pow2(mA + mB)) * (sCM - pow2(mA - mB))) / (2. * eCM);
837 }
838 
839 //--------------------------------------------------------------------------
840 
841 // Get total available phase space, integrating over mass-dependent widths.
842 
843 double HadronWidths::psSize(double eCM, ParticleDataEntry& prodA,
844  ParticleDataEntry& prodB, double lType) const {
845 
846  // Store some important values.
847  int idA = prodA.id(), idB = prodB.id();
848  double m0A = prodA.m0(), m0B = prodB.m0();
849  double mMinA = prodA.mMin(), mMinB = prodB.mMin();
850  double mMaxA = prodA.mMax(), mMaxB = prodB.mMax();
851  bool varA = mMaxA > mMinA, varB = mMaxB > mMinB;
852 
853  if (eCM < mMinA + mMinB)
854  return 0.;
855 
856  double result;
857  bool success = true;
858 
859  // No resonances.
860  if (!varA && !varB)
861  return pow(pCMS(eCM, m0A, m0B), lType);
862 
863  // A is resonance.
864  else if (varA && !varB) {
865  if (eCM <= mMinA + m0B)
866  return 0.;
867 
868  // Integrate mass of A.
869  auto f = [=](double mA) {
870  return pow(pCMS(eCM, mA, m0B), lType) * mDistr(idA, mA); };
871  if (!integrateGauss(result, f, mMinA, min(mMaxA, eCM - m0B)))
872  success = false;
873  }
874 
875  // B is resonance.
876  else if (!varA && varB) {
877  if (eCM <= m0A + mMinB)
878  return 0.;
879 
880  // Integrate mass of B.
881  auto f = [=](double mB) {
882  return pow(pCMS(eCM, m0A, mB), lType) * mDistr(idB, mB); };
883  if (!integrateGauss(result, f, mMinB, min(mMaxB, eCM - m0A)))
884  success = false;
885  }
886 
887  // Both are resonances.
888  else {
889  if (eCM <= mMinA + mMinB)
890  return 0.;
891 
892  // Define integrand of outer integral.
893  auto I = [=, &success](double mA) {
894 
895  // Define integrand of inner integral.
896  auto f = [=](double mB) {
897  return pow(pCMS(eCM, mA, mB), lType)
898  * mDistr(idA, mA) * mDistr(idB, mB); };
899  double res;
900 
901  // Integrate mass of B.
902  if (!integrateGauss(res, f, mMinB, min(mMaxB, eCM - mA)))
903  success = false;
904 
905  return res;
906  };
907 
908  // Integrate mass of A.
909  if (!integrateGauss(result, I, mMinA, min(mMaxA, eCM - mMinB)))
910  success = false;
911  }
912 
913  // Return result if successful.
914  if (success)
915  return result;
916  else {
917  infoPtr->errorMsg("Error in HadronWidths::psSize: Unable to integrate");
918  return NAN;
919  }
920 }
921 
922 //--------------------------------------------------------------------------
923 
924 // Generate parameterization for particle and its decay products if needed.
925 
926 bool HadronWidths::parameterizeRecursive(int id, int precision) {
927 
928  // End recursion if data has already been generated.
929  if (hasData(id))
930  return true;
931 
932  // Get particle entry.
933  ParticleDataEntry& entry = *particleDataPtr->findParticle(id);
934 
935  // Iterate over all two-body channels.
936  for (int iChannel = 0; iChannel < entry.sizeChannels(); ++iChannel) {
937  DecayChannel& channel = entry.channel(iChannel);
938  if (channel.multiplicity() == 2) {
939  auto& prodA = *particleDataPtr->findParticle(channel.product(0));
940  auto& prodB = *particleDataPtr->findParticle(channel.product(1));
941 
942  // Recursive call to parameterize decay product widths if necessary.
943  if (prodA.varWidth() && !hasData(prodA.id()))
944  if (!parameterizeRecursive(prodA.id(), precision)) return false;
945  if (prodB.varWidth() && !hasData(prodB.id()))
946  if (!parameterizeRecursive(prodB.id(), precision)) return false;
947  }
948  }
949 
950  // Perform the actual parameterization of this particle.
951  infoPtr->errorMsg("Info from HadronWidths::parameterizeAll: "
952  "parameterizing", to_string(id), true);
953  return parameterize(id, precision);
954 }
955 
956 //--------------------------------------------------------------------------
957 
958 // Regenerate parameterization for all particles.
959 
960 void HadronWidths::parameterizeAll(int precision) {
961 
962  // Get all particles with varWidth from particle database.
963  vector<ParticleDataEntry*> variableWidthEntries;
964  for (auto& mapEntry : *particleDataPtr) {
965  ParticleDataEntry& entry = mapEntry.second;
966  if (entry.varWidth())
967  variableWidthEntries.push_back(&entry);
968  }
969 
970  // Clear existing data and parameterize new data.
971  entries.clear();
972 
973  for (ParticleDataEntry* entry : variableWidthEntries) {
974  if (!parameterizeRecursive(entry->id(), precision)) {
975  infoPtr->errorMsg("Abort from HadronWidths::parameterizeAll: "
976  "parameterization failed");
977  return;
978  }
979  }
980 }
981 
982 //--------------------------------------------------------------------------
983 
984 // Write all widths data to an xml file.
985 
986 bool HadronWidths::save(ostream& stream) const {
987 
988  if (!stream.good())
989  return false;
990 
991  stream << "\n";
992 
993  for (auto& mapEntry : entries) {
994  int id = mapEntry.first;
995  const HadronWidthEntry& entry = mapEntry.second;
996 
997  // Counter for number of entries on current line, maximum 8 per line.
998  int c = 0;
999 
1000  // Write total width.
1001  stream << "<width id=\"" << id << "\" "
1002  << "left=\"" << entry.width.left() << "\" "
1003  << "right=\"" << entry.width.right() << "\" "
1004  << "data=\" \n";
1005  for (double dataPoint : entry.width.data()) {
1006  stream << " " << dataPoint;
1007  if (++c >= 7) {
1008  c = 0;
1009  stream << " \n";
1010  }
1011  }
1012  stream << "\"/> \n \n";
1013 
1014  // Write partial widths.
1015  for (auto& channelEntry : entry.decayChannels) {
1016  const ResonanceDecayChannel& channel = channelEntry.second;
1017  stream << "<partialWidth id=\"" << id << "\" "
1018  << "products=\"" << channel.prodA << " " << channel.prodB << "\" "
1019  << "lType=\"" << channel.lType << "\" data=\" \n";
1020  c = 0;
1021  for (double dataPoint : channel.partialWidth.data()){
1022  stream << " " << dataPoint;
1023  if (++c >= 7) {
1024  c = 0;
1025  stream << " \n";
1026  }
1027  }
1028  stream << "\"/> \n \n";
1029  }
1030 
1031  stream << " \n \n";
1032  }
1033 
1034  // Done.
1035  return true;
1036 }
1037 
1038 //==========================================================================
1039 
1040 }