8 #include "Pythia8/NucleonExcitations.h"
18 static double pCMS(
double eCM,
double mA,
double mB) {
19 if (eCM <= mA + mB)
return 0;
20 double sCM = eCM * eCM;
21 return sqrt((sCM - pow2(mA + mB)) * (sCM - pow2(mA - mB))) / (2. * eCM);
24 static string attributeValue(
string line,
string attribute) {
25 if (line.find(attribute) == string::npos)
return "";
26 int iBegAttri = line.find(attribute);
27 int iBegQuote = line.find(
"\"", iBegAttri + 1);
28 int iEndQuote = line.find(
"\"", iBegQuote + 1);
29 return line.substr(iBegQuote + 1, iEndQuote - iBegQuote - 1);
33 static int intAttributeValue(
string line,
string attribute) {
34 string valString = attributeValue(line, attribute);
35 if (valString ==
"")
return 0;
36 istringstream valStream(valString);
42 static double doubleAttributeValue(
string line,
string attribute) {
43 string valString = attributeValue(line, attribute);
44 if (valString ==
"")
return 0.;
45 istringstream valStream(valString);
47 valStream >> doubleVal;
51 static void completeTag(istream& stream,
string& line) {
52 while (line.find(
">") == string::npos) {
54 if (!getline(stream, addLine))
break;
55 line +=
" " + addLine;
63 bool NucleonExcitations::init(
string path) {
65 ifstream stream(path);
66 if (!stream.is_open()) {
67 infoPtr->errorMsg(
"Error in NucleonExcitations::init: "
68 "unable to open file", path);
79 bool NucleonExcitations::init(istream& stream) {
82 double eMin = INFINITY;
86 if (!getline(stream, line)) {
87 infoPtr->errorMsg(
"Error in NucleonExcitations::init: "
88 "unable to read file");
93 istringstream(line) >> word1;
95 if (word1 !=
"<header") {
96 infoPtr->errorMsg(
"Error in NucleonExcitations::init: header missing");
99 completeTag(stream, line);
102 double highEnergyThreshold = doubleAttributeValue(line,
"threshold");
103 int sigmaTotalPrecision = intAttributeValue(line,
"sigmaTotalPrecision");
106 while (getline(stream, line)) {
108 if (!(istringstream(line) >> word1))
111 if (word1 ==
"<excitationChannel") {
112 completeTag(stream, line);
115 int maskA = intAttributeValue(line,
"maskA");
116 int maskB = intAttributeValue(line,
"maskB");
117 double left = doubleAttributeValue(line,
"left");
118 double right = doubleAttributeValue(line,
"right");
119 double scaleFactor = doubleAttributeValue(line,
"scaleFactor");
121 istringstream dataStr(attributeValue(line,
"data"));
124 while (dataStr >> currentData)
125 data.push_back(currentData);
132 excitationChannels.push_back(ExcitationChannel {
133 LinearInterpolator(left, right, data), maskA, maskB, scaleFactor });
138 vector<double> sigmaTotPts(sigmaTotalPrecision);
139 double dE = (highEnergyThreshold - eMin) / (sigmaTotalPrecision - 1);
140 for (
int i = 0; i < sigmaTotalPrecision; ++i) {
141 double eCM = eMin + i * dE;
143 for (
auto& channel : excitationChannels)
144 sigma += channel.sigma(eCM);
145 sigmaTotPts[i] = sigma;
147 sigmaTotal = LinearInterpolator(eMin, highEnergyThreshold, sigmaTotPts);
157 bool NucleonExcitations::check() {
160 for (
auto excitationChannel : excitationChannels) {
162 for (
int mask : { excitationChannel.maskA, excitationChannel.maskB })
163 for (
int id : { mask + 2210, mask + 2110 })
164 if (!particleDataPtr->isParticle(
id)) {
165 infoPtr->errorMsg(
"Error in HadronWidths::check: "
166 "excitation is not a particle", std::to_string(
id));
177 bool NucleonExcitations::pickExcitation(
int idA,
int idB,
double eCM,
178 int& idCOut,
double& mCOut,
int& idDOut,
double& mDOut) {
181 if (!(abs(idA) == 2112 || abs(idA) == 2212)
182 || !(abs(idB) == 2112 || abs(idB) == 2212)) {
183 infoPtr->errorMsg(
"Error in NucleonExcitations:pickExcitation: "
184 "excitations are only available for NN collisions");
189 int signA = (idA > 0 ? 1 : -1), signB = (idB > 0 ? 1 : -1);
194 vector<double> sigmas(excitationChannels.size());
195 for (
int i = 0; i < int(sigmas.size()); ++i) {
197 if (eCM < excitationChannels[i].sigma.right())
198 sigmas[i] = excitationChannels[i].sigma(eCM);
201 double mA = particleDataPtr->m0(2210 + excitationChannels[i].maskA);
202 double mB = particleDataPtr->m0(2210 + excitationChannels[i].maskB);
203 sigmas[i] = pCMS(eCM, mA, mB) * excitationChannels[i].scaleFactor;
206 auto& channel = excitationChannels[rndmPtr->pick(sigmas)];
209 int maskA = channel.maskA, maskB = channel.maskB;
210 if (rndmPtr->flat() > 0.5)
214 int idCtmp = maskA + (idA - idA % 10);
215 int idDtmp = maskB + (idB - idB % 10);
219 if (!hadronWidthsPtr->pickMasses(idCtmp, idDtmp, eCM, mCtmp, mDtmp)) {
220 infoPtr->errorMsg(
"Error in NucleonExcitations::pickExcitation: "
221 "failed picking masses",
222 "(for " + to_string(idA) +
" + " + to_string(idB) +
" --> "
223 + to_string(idCtmp) +
" + " + to_string(idDtmp) +
")");
228 idCOut = signA * idCtmp;
229 idDOut = signB * idDtmp;
239 double NucleonExcitations::sigmaExTotal(
double eCM)
const {
241 if (eCM < sigmaTotal.right())
242 return sigmaTotal(eCM);
246 for (
auto channel : excitationChannels) {
247 double mA = particleDataPtr->m0(2210 + channel.maskA);
248 double mB = particleDataPtr->m0(2210 + channel.maskB);
249 sig += channel.scaleFactor * pCMS(eCM, mA, mB);
253 return sig / pCMS(eCM, 0.938, 0.938) / pow2(eCM);
261 double NucleonExcitations::sigmaExPartial(
double eCM,
262 int maskC,
int maskD)
const {
265 maskC = maskC - 10 * ((maskC / 10) % 1000);
266 maskD = maskD - 10 * ((maskD / 10) % 1000);
269 if (maskD == 0002 || (maskD == 0004 && maskC > 0004))
273 for (
auto& channel : excitationChannels)
274 if (channel.maskA == maskC && channel.maskB == maskD) {
276 if (eCM < channel.sigma.right())
277 return channel.sigma(eCM);
280 double mA = particleDataPtr->m0(2210 + channel.maskA);
281 double mB = particleDataPtr->m0(2210 + channel.maskB);
282 return channel.scaleFactor / pow2(eCM)
283 * pCMS(eCM, mA, mB) / pCMS(eCM, 0.938, 0.938);
294 vector<pair<int, int>> NucleonExcitations::getChannels()
const {
295 vector<pair<int, int>> result;
296 for (
auto channel : excitationChannels)
297 result.push_back(make_pair(channel.maskA, channel.maskB));
305 vector<int> NucleonExcitations::getExcitationMasks()
const {
308 for (
auto& kvPair : *particleDataPtr) {
309 int id = kvPair.first;
310 int quarkContent = ((
id / 10) % 1000);
311 int mask =
id - 10 * quarkContent;
314 if ( ((mask == 0004) || (mask >= 10000 && mask < 1000000))
315 && quarkContent == 221 )
316 results.push_back(mask);
326 double NucleonExcitations::sigmaCalc(
double eCM,
int maskC,
int maskD)
const {
329 int quarkContentC = (maskC / 10) % 1000, quarkContentD = (maskD / 10) % 1000;
330 maskC -= 10 * quarkContentC;
331 maskD -= 10 * quarkContentD;
332 ParticleDataEntry* entryC = particleDataPtr->findParticle(2210 + maskC);
333 ParticleDataEntry* entryD = particleDataPtr->findParticle(2210 + maskD);
336 if (eCM < entryC->mMin() + entryD->mMin())
340 double matrixElement;
341 if (maskC == 0002 && maskD == 0004) {
342 constexpr
double A = 40000, mD2 = pow2(1.232), GammaD2 = pow2(0.115);
343 matrixElement = A * mD2 * GammaD2 /
344 (pow2(eCM * eCM - mD2) + mD2 * GammaD2);
346 else if (maskC == 0004 && maskD == 0004)
349 double mD = particleDataPtr->m0(2210 + maskD);
353 if (particleDataPtr->isParticle(2220 + maskD))
363 matrixElement = A / (pow2(mD - mC) * pow2(mD + mC));
367 return entryC->spinType() * entryD->spinType() * matrixElement
368 * psSize(eCM, *entryC, *entryD) / pCMS(eCM, 0.938, 0.938) / pow2(eCM);
375 bool NucleonExcitations::parameterizeAll(
int precision,
double threshold) {
378 infoPtr->errorMsg(
"Error in NucleonExcitations::parameterizeAll: "
379 "precision must be at least 2");
383 double mN = particleDataPtr->m0(2212), mD = particleDataPtr->m0(2214);
386 double scaleFactorN = 2.;
389 bool check = integrateGauss(scaleFactorD, [&](
double m) {
390 return hadronWidthsPtr->mDistr(2214, m);
391 }, particleDataPtr->mMin(2214), particleDataPtr->mMax(2214));
393 infoPtr->errorMsg(
"Abort from NucleonExcitations::parameterizeAll: "
394 "unable to integrate excitation mass distribution",
"2214");
400 excitationChannels.clear();
401 for (
auto maskEx : getExcitationMasks()) {
403 int idEx = 2210 + maskEx;
404 infoPtr->errorMsg(
"Info from NucleonExcitations::parameterizeAll: "
405 "parameterizing", to_string(idEx),
true);
408 ParticleDataEntry* entry = particleDataPtr->findParticle(idEx);
409 double mEx = entry->m0(), mMinEx = entry->mMin();
410 bool isDelta = particleDataPtr->isParticle(2220 + maskEx);
413 double scaleFactorEx;
414 check = integrateGauss(scaleFactorEx, [&](
double m) {
415 return hadronWidthsPtr->mDistr(idEx, m);
416 }, entry->mMin(), entry->mMax());
419 infoPtr->errorMsg(
"Abort from NucleonExcitations::parameterizeAll: "
420 "unable to integrate excitation mass distribution", to_string(idEx));
423 scaleFactorEx *= entry->spinType();
426 double eMin = mN + mMinEx;
427 double de = (threshold - eMin) / (precision - 1);
428 vector<double> dataPointsNX(precision);
429 for (
int ie = 0; ie < precision; ++ie) {
430 double eNow = eMin + de * ie;
431 dataPointsNX[ie] = sigmaCalc(eNow, 0002, maskEx);
434 double scaleN = (maskEx == 0004) ? 0.
435 : scaleFactorN * scaleFactorEx * (isDelta ? 12.0 : 6.3)
436 / (pow2(mN - mEx) * pow2(mN + mEx));
437 excitationChannels.push_back(ExcitationChannel {
438 LinearInterpolator(eMin, threshold, dataPointsNX),
444 de = (threshold - eMin) / (precision - 1);
445 vector<double> dataPointsDX(precision);
446 for (
int ie = 0; ie < precision; ++ie) {
447 double eNow = eMin + de * ie;
448 dataPointsDX[ie] = sigmaCalc(eNow, 0004, maskEx);
451 double scaleD = scaleFactorD * scaleFactorEx *
452 (maskEx == 0004 ? 2.8 : 3.5 / (pow2(mD - mEx) * pow2(mD + mEx)));
453 excitationChannels.push_back(ExcitationChannel {
454 LinearInterpolator(eMin, threshold, dataPointsDX),
460 vector<double> sigmaTotPts(precision);
461 double eMin = mN + mD;
462 double de = (threshold - eMin) / (precision - 1);
463 for (
int ie = 0; ie < precision; ++ie) {
464 double eNow = eMin + de * ie;
466 for (
auto& channel : excitationChannels)
467 sigmaTotPts[ie] += channel.sigma(eNow);
469 sigmaTotal = LinearInterpolator(eMin, threshold, sigmaTotPts);
479 double NucleonExcitations::psSize(
double eCM, ParticleDataEntry& prodA,
480 ParticleDataEntry& prodB)
const {
483 int idA = prodA.id(), idB = prodB.id();
484 double m0A = prodA.m0(), m0B = prodB.m0();
485 double mMinA = prodA.mMin(), mMinB = prodB.mMin();
486 double mMaxA = prodA.mMax(), mMaxB = prodB.mMax();
487 bool varA = mMaxA > mMinA, varB = mMaxB > mMinB;
489 if (eCM < mMinA + mMinB)
497 return pCMS(eCM, m0A, m0B);
500 else if (varA && !varB) {
501 if (eCM <= mMinA + m0B)
505 auto f = [=](
double mA) {
506 return pCMS(eCM, mA, m0B) * hadronWidthsPtr->mDistr(idA, mA); };
507 if (!integrateGauss(result, f, mMinA, min(mMaxA, eCM - m0B)))
512 else if (!varA && varB) {
513 if (eCM <= m0A + mMinB)
517 auto f = [=](
double mB) {
518 return pCMS(eCM, m0A, mB) * hadronWidthsPtr->mDistr(idB, mB); };
519 if (!integrateGauss(result, f, mMinB, min(mMaxB, eCM - m0A)))
525 if (eCM <= mMinA + mMinB)
529 auto I = [=, &success](
double mA) {
532 auto f = [=](
double mB) {
533 return pCMS(eCM, mA, mB)
534 * hadronWidthsPtr->mDistr(idA, mA)
535 * hadronWidthsPtr->mDistr(idB, mB); };
539 if (!integrateGauss(res, f, mMinB, min(mMaxB, eCM - mA)))
546 if (!integrateGauss(result, I, mMinA, min(mMaxA, eCM - mMinB)))
554 infoPtr->errorMsg(
"Error in HadronWidths::psSize: Unable to integrate");
563 bool NucleonExcitations::save(ostream& stream)
const {
570 <<
"threshold=\"" << sigmaTotal.right() <<
"\" "
571 <<
"sigmaTotalPrecision=\"" << sigmaTotal.data().size() <<
"\" /> "
575 for (
auto& channel : excitationChannels) {
576 stream <<
"<excitationChannel "
577 <<
"maskA=\"" << channel.maskA <<
"\" "
578 <<
"maskB=\"" << channel.maskB <<
"\" "
579 <<
"left=\"" << channel.sigma.left() <<
"\" "
580 <<
"right=\"" << channel.sigma.right() <<
"\" "
581 <<
"scaleFactor=\"" << channel.scaleFactor <<
"\" "
584 for (
double dataPoint : channel.sigma.data())
585 stream << dataPoint <<
" ";
586 stream <<
"\n /> \n \n";