Line data Source code
1 : #include "crpropa/module/PhotoPionProduction.h"
2 : #include "crpropa/Units.h"
3 : #include "crpropa/ParticleID.h"
4 : #include "crpropa/Random.h"
5 :
6 : #include "kiss/convert.h"
7 : #include "kiss/logger.h"
8 : #include "sophia.h"
9 :
10 : #include <limits>
11 : #include <cmath>
12 : #include <sstream>
13 : #include <fstream>
14 : #include <stdexcept>
15 :
16 : namespace crpropa {
17 :
18 11 : PhotoPionProduction::PhotoPionProduction(ref_ptr<PhotonField> field, bool photons, bool neutrinos, bool electrons, bool antiNucleons, double l, bool redshift) {
19 11 : havePhotons = photons;
20 11 : haveNeutrinos = neutrinos;
21 11 : haveElectrons = electrons;
22 11 : haveAntiNucleons = antiNucleons;
23 11 : haveRedshiftDependence = redshift;
24 11 : limit = l;
25 11 : setPhotonField(field);
26 11 : }
27 :
28 22 : void PhotoPionProduction::setPhotonField(ref_ptr<PhotonField> field) {
29 22 : photonField = field;
30 22 : std::string fname = photonField->getFieldName();
31 22 : if (haveRedshiftDependence) {
32 0 : if (photonField->hasRedshiftDependence() == false){
33 0 : std::cout << "PhotoPionProduction: tabulated redshift dependence not needed for " + fname + ", switching off" << std::endl;
34 0 : haveRedshiftDependence = false;
35 : }
36 : else {
37 0 : KISS_LOG_WARNING << "PhotoPionProduction: You are using the 2-dimensional tabulated redshift evolution, which is not available for other interactions. To be consistent across all interactions you may deactivate this <setHaveRedshiftDependence(False)>.";
38 : }
39 : }
40 :
41 22 : setDescription("PhotoPionProduction: " + fname);
42 22 : if (haveRedshiftDependence){
43 0 : initRate(getDataPath("PhotoPionProduction/rate_" + fname.replace(0, 3, "IRBz") + ".txt"));
44 : }
45 : else
46 66 : initRate(getDataPath("PhotoPionProduction/rate_" + fname + ".txt"));
47 22 : }
48 :
49 1 : void PhotoPionProduction::setHavePhotons(bool b) {
50 1 : havePhotons = b;
51 1 : }
52 :
53 0 : void PhotoPionProduction::setHaveElectrons(bool b) {
54 0 : haveElectrons = b;
55 0 : }
56 :
57 0 : void PhotoPionProduction::setHaveNeutrinos(bool b) {
58 0 : haveNeutrinos = b;
59 0 : }
60 :
61 0 : void PhotoPionProduction::setHaveAntiNucleons(bool b) {
62 0 : haveAntiNucleons = b;
63 0 : }
64 :
65 0 : void PhotoPionProduction::setHaveRedshiftDependence(bool b) {
66 0 : haveRedshiftDependence = b;
67 0 : setPhotonField(photonField);
68 0 : }
69 :
70 0 : void PhotoPionProduction::setLimit(double l) {
71 0 : limit = l;
72 0 : }
73 :
74 22 : void PhotoPionProduction::initRate(std::string filename) {
75 : // clear previously loaded tables
76 22 : tabLorentz.clear();
77 22 : tabRedshifts.clear();
78 22 : tabProtonRate.clear();
79 22 : tabNeutronRate.clear();
80 :
81 22 : std::ifstream infile(filename.c_str());
82 22 : if (!infile.good())
83 0 : throw std::runtime_error("PhotoPionProduction: could not open file " + filename);
84 :
85 22 : if (haveRedshiftDependence) {
86 : double zOld = -1, aOld = -1;
87 0 : while (infile.good()) {
88 0 : if (infile.peek() == '#') {
89 0 : infile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
90 0 : continue;
91 : }
92 : double z, a, b, c;
93 : infile >> z >> a >> b >> c;
94 0 : if (!infile)
95 : break;
96 0 : if (z > zOld) {
97 0 : tabRedshifts.push_back(z);
98 0 : zOld = z;
99 : }
100 0 : if (a > aOld) {
101 0 : tabLorentz.push_back(pow(10, a));
102 0 : aOld = a;
103 : }
104 0 : tabProtonRate.push_back(b / Mpc);
105 0 : tabNeutronRate.push_back(c / Mpc);
106 : }
107 : } else {
108 5610 : while (infile.good()) {
109 5610 : if (infile.peek() == '#') {
110 66 : infile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
111 66 : continue;
112 : }
113 : double a, b, c;
114 : infile >> a >> b >> c;
115 5544 : if (!infile)
116 : break;
117 5522 : tabLorentz.push_back(pow(10, a));
118 5522 : tabProtonRate.push_back(b / Mpc);
119 5522 : tabNeutronRate.push_back(c / Mpc);
120 : }
121 : }
122 :
123 22 : infile.close();
124 22 : }
125 :
126 65838 : double PhotoPionProduction::nucleonMFP(double gamma, double z, bool onProton) const {
127 65838 : const std::vector<double> &tabRate = (onProton)? tabProtonRate : tabNeutronRate;
128 :
129 : // scale nucleus energy instead of background photon energy
130 65838 : gamma *= (1 + z);
131 65838 : if (gamma < tabLorentz.front() or (gamma > tabLorentz.back()))
132 : return std::numeric_limits<double>::max();
133 :
134 : double rate;
135 65838 : if (haveRedshiftDependence)
136 0 : rate = interpolate2d(z, gamma, tabRedshifts, tabLorentz, tabRate);
137 : else
138 65838 : rate = interpolate(gamma, tabLorentz, tabRate) * photonField->getRedshiftScaling(z);
139 :
140 : // cosmological scaling
141 65838 : rate *= pow_integer<2>(1 + z);
142 :
143 65838 : return 1. / rate;
144 : }
145 :
146 65838 : double PhotoPionProduction::nucleiModification(int A, int X) const {
147 65838 : if (A == 1)
148 : return 1.;
149 1161 : if (A <= 8)
150 345 : return 0.85 * pow(X, 2. / 3.);
151 816 : return 0.85 * X;
152 : }
153 :
154 65205 : void PhotoPionProduction::process(Candidate *candidate) const {
155 65205 : double step = candidate->getCurrentStep();
156 65205 : double z = candidate->getRedshift();
157 : // the loop is processed at least once for limiting the next step
158 : do {
159 : // check if nucleus
160 65260 : int id = candidate->current.getId();
161 65260 : if (!isNucleus(id))
162 : return;
163 :
164 : // find interaction with minimum random distance
165 65259 : Random &random = Random::instance();
166 : double randDistance = std::numeric_limits<double>::max();
167 : double meanFreePath;
168 : double totalRate = 0;
169 : bool onProton = true; // interacting particle: proton or neutron
170 :
171 65259 : int A = massNumber(id);
172 65259 : int Z = chargeNumber(id);
173 65259 : int N = A - Z;
174 65259 : double gamma = candidate->current.getLorentzFactor();
175 :
176 : // check for interaction on protons
177 65259 : if (Z > 0) {
178 64707 : meanFreePath = nucleonMFP(gamma, z, true) / nucleiModification(A, Z);
179 64707 : randDistance = -log(random.rand()) * meanFreePath;
180 64707 : totalRate += 1. / meanFreePath;
181 : }
182 : // check for interaction on neutrons
183 65259 : if (N > 0) {
184 1131 : meanFreePath = nucleonMFP(gamma, z, false) / nucleiModification(A, N);
185 1131 : totalRate += 1. / meanFreePath;
186 1131 : double d = -log(random.rand()) * meanFreePath;
187 1131 : if (d < randDistance) {
188 : randDistance = d;
189 : onProton = false;
190 : }
191 : }
192 :
193 : // check if interaction does not happen
194 65259 : if (step < randDistance) {
195 65204 : if (totalRate > 0.)
196 65204 : candidate->limitNextStep(limit / totalRate);
197 65204 : return;
198 : }
199 :
200 : // interact and repeat with remaining step
201 55 : performInteraction(candidate, onProton);
202 55 : step -= randDistance;
203 55 : } while (step > 0);
204 : }
205 :
206 65 : void PhotoPionProduction::performInteraction(Candidate *candidate, bool onProton) const {
207 65 : int id = candidate->current.getId();
208 65 : int A = massNumber(id);
209 65 : int Z = chargeNumber(id);
210 65 : double E = candidate->current.getEnergy();
211 65 : double EpA = E / A;
212 65 : double z = candidate->getRedshift();
213 :
214 : // SOPHIA simulates interactions only for protons / neutrons.
215 : // For anti-protons / neutrons assume charge symmetry and change all
216 : // interaction products from particle <--> anti-particle (sign)
217 65 : int sign = (id > 0) ? 1 : -1;
218 :
219 : // check if below SOPHIA's energy threshold
220 130 : double E_threshold = (photonField->getFieldName() == "CMB") ? 3.72e18 * eV : 5.83e15 * eV;
221 65 : if (EpA * (1 + z) < E_threshold)
222 0 : return;
223 :
224 : // SOPHIA - input:
225 65 : int nature = 1 - static_cast<int>(onProton); // 0=proton, 1=neutron
226 65 : double Ein = EpA / GeV; // GeV is the SOPHIA standard unit
227 65 : double eps = sampleEps(onProton, EpA, z) / GeV; // GeV for SOPHIA
228 :
229 : // SOPHIA - output:
230 : double outputEnergy[5][2000]; // [GeV/c, GeV/c, GeV/c, GeV, GeV/c^2]
231 : int outPartID[2000];
232 : int nParticles;
233 :
234 130 : #pragma omp critical
235 : {
236 65 : sophiaevent_(nature, Ein, eps, outputEnergy, outPartID, nParticles);
237 : }
238 :
239 65 : Random &random = Random::instance();
240 65 : Vector3d pos = random.randomInterpolatedPosition(candidate->previous.getPosition(), candidate->current.getPosition());
241 : std::vector<int> pnType; // filled with either 13 (proton) or 14 (neutron)
242 : std::vector<double> pnEnergy; // corresponding energies of proton or neutron
243 65 : if (nParticles == 0)
244 : return;
245 482 : for (int i = 0; i < nParticles; i++) { // loop over out-going particles
246 417 : double Eout = outputEnergy[3][i] * GeV; // only the energy is used; could be changed for more detail
247 417 : int pType = outPartID[i];
248 417 : switch (pType) {
249 66 : case 13: // proton
250 : case 14: // neutron
251 : // proton and neutron data is taken to determine primary particle in a later step
252 66 : pnType.push_back(pType);
253 66 : pnEnergy.push_back(Eout);
254 : break;
255 1 : case -13: // anti-proton
256 : case -14: // anti-neutron
257 1 : if (haveAntiNucleons)
258 : try
259 : {
260 0 : candidate->addSecondary(-sign * nucleusId(1, 14 + pType), Eout, pos, 1., interactionTag);
261 : }
262 0 : catch (std::runtime_error &e)
263 : {
264 0 : KISS_LOG_ERROR<< "Something went wrong in the PhotoPionProduction (anti-nucleon production)\n" << "Something went wrong in the PhotoPionProduction\n"<< "Please report this error on https://github.com/CRPropa/CRPropa3/issues including your simulation setup and the following random seed:\n" << Random::instance().getSeed_base64();
265 0 : throw;
266 0 : }
267 : break;
268 90 : case 1: // photon
269 90 : if (havePhotons)
270 14 : candidate->addSecondary(22, Eout, pos, 1., interactionTag);
271 : break;
272 48 : case 2: // positron
273 48 : if (haveElectrons)
274 2 : candidate->addSecondary(sign * -11, Eout, pos, 1., interactionTag);
275 : break;
276 17 : case 3: // electron
277 17 : if (haveElectrons)
278 2 : candidate->addSecondary(sign * 11, Eout, pos, 1., interactionTag);
279 : break;
280 48 : case 15: // nu_e
281 48 : if (haveNeutrinos)
282 2 : candidate->addSecondary(sign * 12, Eout, pos, 1., interactionTag);
283 : break;
284 17 : case 16: // anti-nu_e
285 17 : if (haveNeutrinos)
286 2 : candidate->addSecondary(sign * -12, Eout, pos, 1., interactionTag);
287 : break;
288 65 : case 17: // nu_mu
289 65 : if (haveNeutrinos)
290 4 : candidate->addSecondary(sign * 14, Eout, pos, 1., interactionTag);
291 : break;
292 65 : case 18: // anti-nu_mu
293 65 : if (haveNeutrinos)
294 4 : candidate->addSecondary(sign * -14, Eout, pos, 1., interactionTag);
295 : break;
296 0 : default:
297 0 : throw std::runtime_error("PhotoPionProduction: unexpected particle " + kiss::str(pType));
298 : }
299 : }
300 65 : double maxEnergy = *std::max_element(pnEnergy.begin(), pnEnergy.end()); // criterion for being declared primary
301 131 : for (int i = 0; i < pnEnergy.size(); ++i) {
302 66 : if (pnEnergy[i] == maxEnergy) { // nucleon is primary particle
303 65 : if (A == 1) {
304 : // single interacting nucleon
305 61 : candidate->current.setEnergy(pnEnergy[i]);
306 : try
307 : {
308 61 : candidate->current.setId(sign * nucleusId(1, 14 - pnType[i]));
309 : }
310 0 : catch (std::runtime_error &e)
311 : {
312 0 : KISS_LOG_ERROR<< "Something went wrong in the PhotoPionProduction (primary particle, A==1)\n" << "Please report this error on https://github.com/CRPropa/CRPropa3/issues including your simulation setup and the following random seed:\n" << Random::instance().getSeed_base64();
313 0 : throw;
314 0 : }
315 : } else {
316 : // interacting nucleon is part of nucleus: it is emitted from the nucleus
317 4 : candidate->current.setEnergy(E - EpA);
318 : try
319 : {
320 4 : candidate->current.setId(sign * nucleusId(A - 1, Z - int(onProton)));
321 4 : candidate->addSecondary(sign * nucleusId(1, 14 - pnType[i]), pnEnergy[i], pos, 1., interactionTag);
322 : }
323 0 : catch (std::runtime_error &e)
324 : {
325 0 : KISS_LOG_ERROR<< "Something went wrong in the PhotoPionProduction (primary particle, A!=1)\n" << "Please report this error on https://github.com/CRPropa/CRPropa3/issues including your simulation setup and the following random seed:\n" << Random::instance().getSeed_base64();
326 0 : throw;
327 0 : }
328 : }
329 : } else { // nucleon is secondary proton or neutron
330 1 : candidate->addSecondary(sign * nucleusId(1, 14 - pnType[i]), pnEnergy[i], pos, 1., interactionTag);
331 : }
332 : }
333 : }
334 :
335 0 : double PhotoPionProduction::lossLength(int id, double gamma, double z) {
336 0 : int A = massNumber(id);
337 0 : int Z = chargeNumber(id);
338 0 : int N = A - Z;
339 :
340 : double lossRate = 0;
341 0 : if (Z > 0)
342 0 : lossRate += 1 / nucleonMFP(gamma, z, true) * nucleiModification(A, Z);
343 0 : if (N > 0)
344 0 : lossRate += 1 / nucleonMFP(gamma, z, false) * nucleiModification(A, N);
345 :
346 : // approximate the relative energy loss
347 : // - nucleons keep the fraction of mass to delta-resonance mass
348 : // - nuclei lose the energy 1/A the interacting nucleon is carrying
349 0 : double relativeEnergyLoss = (A == 1) ? 1 - 938. / 1232. : 1. / A;
350 0 : lossRate *= relativeEnergyLoss;
351 :
352 : // scaling factor: interaction rate --> energy loss rate
353 0 : lossRate *= (1 + z);
354 :
355 0 : return 1. / lossRate;
356 : }
357 :
358 0 : SophiaEventOutput PhotoPionProduction::sophiaEvent(bool onProton, double Ein, double eps) const {
359 : // SOPHIA - input:
360 0 : int nature = 1 - static_cast<int>(onProton); // 0=proton, 1=neutron
361 0 : Ein /= GeV; // GeV is the SOPHIA standard unit
362 0 : eps /= GeV; // GeV for SOPHIA
363 :
364 : // SOPHIA - output:
365 : double outputEnergy[5][2000]; // [Px GeV/c, Py GeV/c, Pz GeV/c, E GeV, m0 GeV/c^2]
366 : int outPartID[2000];
367 : int nParticles;
368 :
369 0 : sophiaevent_(nature, Ein, eps, outputEnergy, outPartID, nParticles);
370 :
371 : // convert SOPHIA IDs to PDG naming convention & create particles
372 : SophiaEventOutput output;
373 0 : output.nParticles = nParticles;
374 0 : for (int i = 0; i < nParticles; ++i) {
375 0 : int id = 0;
376 0 : int partType = outPartID[i];
377 0 : switch (partType) {
378 0 : case 13: // proton
379 : case 14: // neutron
380 0 : id = nucleusId(1, 14 - partType);
381 0 : break;
382 0 : case -13: // anti-proton
383 : case -14: // anti-neutron
384 0 : id = -nucleusId(1, 14 + partType);
385 0 : break;
386 0 : case 1: // photon
387 0 : id = 22;
388 0 : break;
389 0 : case 2: // positron
390 0 : id = -11;
391 0 : break;
392 0 : case 3: // electron
393 0 : id = 11;
394 0 : break;
395 0 : case 15: // nu_e
396 0 : id = 12;
397 0 : break;
398 0 : case 16: // anti-nu_e
399 0 : id = -12;
400 0 : break;
401 0 : case 17: // nu_mu
402 0 : id = 14;
403 0 : break;
404 0 : case 18: // anti-nu_mu
405 0 : id = -14;
406 0 : break;
407 0 : default:
408 0 : throw std::runtime_error("PhotoPionProduction: unexpected particle " + kiss::str(partType));
409 : }
410 0 : output.energy.push_back(outputEnergy[3][i] * GeV); // only the energy is used; could be changed for more detail
411 0 : output.id.push_back(id);
412 : }
413 0 : return output;
414 0 : }
415 :
416 65 : double PhotoPionProduction::sampleEps(bool onProton, double E, double z) const {
417 : // sample eps between epsMin ... epsMax
418 65 : double Ein = E / GeV;
419 130 : double epsMin = std::max(photonField -> getMinimumPhotonEnergy(z) / eV, epsMinInteraction(onProton, Ein));
420 65 : double epsMax = photonField -> getMaximumPhotonEnergy(z) / eV;
421 65 : double pEpsMax = probEpsMax(onProton, Ein, z, epsMin, epsMax);
422 :
423 65 : Random &random = Random::instance();
424 47452 : for (int i = 0; i < 1000000; i++) {
425 47452 : double eps = epsMin + random.rand() * (epsMax - epsMin);
426 47452 : double pEps = probEps(eps, onProton, Ein, z);
427 47452 : if (random.rand() * pEpsMax < pEps)
428 65 : return eps * eV;
429 : }
430 0 : throw std::runtime_error("error: no photon found in sampleEps, please make sure that photon field provides photons for the interaction by adapting the energy range of the tabulated photon field.");
431 : }
432 :
433 65 : double PhotoPionProduction::epsMinInteraction(bool onProton, double Ein) const {
434 : // labframe energy of least energetic photon where PPP can occur
435 : // this kind-of ties samplingEps to the PPP and SOPHIA
436 65 : const double m = mass(onProton);
437 65 : const double p = momentum(onProton, Ein);
438 65 : double epsMin = 1.e9 * (1.1646 - m * m) / 2. / (Ein + p); // eV
439 65 : return epsMin;
440 : }
441 :
442 66 : double PhotoPionProduction::probEpsMax(bool onProton, double Ein, double z, double epsMin, double epsMax) const {
443 : // find pEpsMax by testing photon energies (eps) for their interaction
444 : // probabilities (p) in order to find the maximum (max) probability
445 : const int nrSteps = 100;
446 : double pEpsMaxTested = 0.;
447 : double step = 0.;
448 66 : if (sampleLog){
449 : // sample in logspace with stepsize that is at max Δlog(E/eV) = 0.01 or otherwise dep. on size of energy range with nrSteps+1 steps log. equidis. spaced
450 66 : step = std::min(0.01, std::log10(epsMax / epsMin) / nrSteps);
451 : } else
452 0 : step = (epsMax - epsMin) / nrSteps;
453 :
454 : double epsDummy = 0.;
455 : int i = 0;
456 16509 : while (epsDummy < epsMax) {
457 16443 : if (sampleLog)
458 16443 : epsDummy = epsMin * pow(10, step * i);
459 : else
460 0 : epsDummy = epsMin + step * i;
461 16443 : double p = probEps(epsDummy, onProton, Ein, z);
462 16443 : if(p > pEpsMaxTested)
463 : pEpsMaxTested = p;
464 16443 : i++;
465 : }
466 : // the following factor corrects for only trying to find the maximum on nrIteration photon energies
467 : // the factor should be determined in convergence tests
468 66 : double pEpsMax = pEpsMaxTested * correctionFactor;
469 :
470 66 : if(pEpsMax == 0) {
471 0 : KISS_LOG_WARNING << "pEpsMax is 0 in the following configuration: \n"
472 0 : << "\t" << "onProton: " << onProton << "\n"
473 0 : << "\t" << "Ein: " << Ein << " [GeV] \n"
474 0 : << "\t" << "epsRange [eV] " << epsMin << "\t" << epsMax << "\n"
475 0 : << "\t" << "redshift: " << z << "\n"
476 0 : << "\t" << "sample Log " << sampleLog << " with step " << step << " [eV] \n";
477 : }
478 :
479 66 : return pEpsMax;
480 : }
481 :
482 63895 : double PhotoPionProduction::probEps(double eps, bool onProton, double Ein, double z) const {
483 : // probEps returns "probability to encounter a photon of energy eps", given a primary nucleon
484 : // note, probEps does not return a normalized probability [0,...,1]
485 63895 : double photonDensity = photonField->getPhotonDensity(eps * eV, z) * ccm / eps;
486 63895 : if (photonDensity != 0.) {
487 63873 : const double p = momentum(onProton, Ein);
488 63873 : const double sMax = mass(onProton) * mass(onProton) + 2. * eps * (Ein + p) / 1.e9;
489 63873 : if (sMax <= sMin())
490 : return 0;
491 574263 : double sIntegr = gaussInt([this, onProton](double s) { return this->functs(s, onProton); }, sMin(), sMax);
492 63807 : return photonDensity * sIntegr / eps / eps / p / 8. * 1.e18 * 1.e6;
493 : }
494 : return 0;
495 : }
496 :
497 63938 : double PhotoPionProduction::momentum(bool onProton, double Ein) const {
498 63938 : const double m = mass(onProton);
499 63938 : const double momentumHadron = sqrt(Ein * Ein - m * m); // GeV/c
500 63938 : return momentumHadron;
501 : }
502 :
503 1020912 : double PhotoPionProduction::crossection(double eps, bool onProton) const {
504 1020912 : const double m = mass(onProton);
505 1020912 : const double s = m * m + 2. * m * eps;
506 1020912 : if (s < sMin())
507 : return 0.;
508 : double cross_res = 0.;
509 : double cross_dir = 0.;
510 : double cross_dir1 = 0.;
511 : double cross_dir2 = 0.;
512 : double sig_res[9];
513 :
514 : // first half of array: 9x proton resonance data | second half of array 9x neutron resonance data
515 : static const double AMRES[18] = {1.231, 1.440, 1.515, 1.525, 1.675, 1.680, 1.690, 1.895, 1.950, 1.231, 1.440, 1.515, 1.525, 1.675, 1.675, 1.690, 1.895, 1.950};
516 : static const double BGAMMA[18] = {5.6, 0.5, 4.6, 2.5, 1.0, 2.1, 2.0, 0.2, 1.0, 6.1, 0.3, 4.0, 2.5, 0.0, 0.2, 2.0, 0.2, 1.0};
517 : static const double WIDTH[18] = {0.11, 0.35, 0.11, 0.1, 0.16, 0.125, 0.29, 0.35, 0.3, 0.11, 0.35, 0.11, 0.1, 0.16, 0.150, 0.29, 0.35, 0.3};
518 : static const double RATIOJ[18] = {1., 0.5, 1., 0.5, 0.5, 1.5, 1., 1.5, 2., 1., 0.5, 1., 0.5, 0.5, 1.5, 1., 1.5, 2.};
519 : static const double AM2[2] = {0.882792, 0.880351};
520 :
521 1020912 : const int idx = onProton? 0 : 9;
522 : double SIG0[9];
523 10209120 : for (int i = 0; i < 9; ++i) {
524 9188208 : SIG0[i] = 4.893089117 / AM2[int(onProton)] * RATIOJ[i + idx] * BGAMMA[i + idx];
525 : }
526 1020912 : if (eps <= 10.) {
527 389793 : cross_res = breitwigner(SIG0[0], WIDTH[0 + idx], AMRES[0 + idx], eps, onProton) * Ef(eps, 0.152, 0.17);
528 : sig_res[0] = cross_res;
529 3508137 : for (int i = 1; i < 9; ++i) {
530 3118344 : sig_res[i] = breitwigner(SIG0[i], WIDTH[i + idx], AMRES[i + idx], eps, onProton) * Ef(eps, 0.15, 0.38);
531 3118344 : cross_res += sig_res[i];
532 : }
533 : // direct channel
534 389793 : if ((eps > 0.1) && (eps < 0.6)) {
535 141887 : cross_dir1 = 92.7 * Pl(eps, 0.152, 0.25, 2.0) // single pion production
536 141887 : + 40. * std::exp(-(eps - 0.29) * (eps - 0.29) / 0.002)
537 141887 : - 15. * std::exp(-(eps - 0.37) * (eps - 0.37) / 0.002);
538 : } else {
539 247906 : cross_dir1 = 92.7 * Pl(eps, 0.152, 0.25, 2.0); // single pion production
540 : }
541 389793 : cross_dir2 = 37.7 * Pl(eps, 0.4, 0.6, 2.0); // double pion production
542 389793 : cross_dir = cross_dir1 + cross_dir2;
543 : }
544 : // fragmentation 2:
545 1020912 : double cross_frag2 = onProton? 80.3 : 60.2;
546 1020912 : cross_frag2 *= Ef(eps, 0.5, 0.1) * std::pow(s, -0.34);
547 : // multipion production/fragmentation 1 cross section
548 : double cs_multidiff = 0.;
549 : double cs_multi = 0.;
550 : double cross_diffr1 = 0.;
551 : double cross_diffr2 = 0.;
552 : double cross_diffr = 0.;
553 1020912 : if (eps > 0.85) {
554 852363 : double ss1 = (eps - 0.85) / 0.69;
555 852363 : double ss2 = onProton? 29.3 : 26.4;
556 852363 : ss2 *= std::pow(s, -0.34) + 59.3 * std::pow(s, 0.095);
557 852363 : cs_multidiff = (1. - std::exp(-ss1)) * ss2;
558 852363 : cs_multi = 0.89 * cs_multidiff;
559 : // diffractive scattering:
560 : cross_diffr1 = 0.099 * cs_multidiff;
561 : cross_diffr2 = 0.011 * cs_multidiff;
562 852363 : cross_diffr = 0.11 * cs_multidiff;
563 : // **************************************
564 852363 : ss1 = std::pow(eps - 0.85, 0.75) / 0.64;
565 852363 : ss2 = 74.1 * std::pow(eps, -0.44) + 62. * std::pow(s, 0.08);
566 852363 : double cs_tmp = 0.96 * (1. - std::exp(-ss1)) * ss2;
567 852363 : cross_diffr1 = 0.14 * cs_tmp;
568 852363 : cross_diffr2 = 0.013 * cs_tmp;
569 852363 : double cs_delta = cross_frag2 - (cross_diffr1 + cross_diffr2 - cross_diffr);
570 852363 : if (cs_delta < 0.) {
571 : cross_frag2 = 0.;
572 0 : cs_multi += cs_delta;
573 : } else {
574 : cross_frag2 = cs_delta;
575 : }
576 : cross_diffr = cross_diffr1 + cross_diffr2;
577 852363 : cs_multidiff = cs_multi + cross_diffr;
578 : // in the original SOPHIA code, here is a switch for the return argument.
579 : // Here, only one case (compare in SOPHIA: NDIR=3) is needed.
580 : }
581 1020912 : return cross_res + cross_dir + cs_multidiff + cross_frag2;
582 : }
583 :
584 779586 : double PhotoPionProduction::Pl(double eps, double epsTh, double epsMax, double alpha) const {
585 779586 : if (epsTh > eps)
586 : return 0.;
587 665380 : const double a = alpha * epsMax / epsTh;
588 665380 : const double prod1 = std::pow((eps - epsTh) / (epsMax - epsTh), a - alpha);
589 665380 : const double prod2 = std::pow(eps / epsMax, -a);
590 665380 : return prod1 * prod2;
591 : }
592 :
593 4529049 : double PhotoPionProduction::Ef(double eps, double epsTh, double w) const {
594 4529049 : const double wTh = w + epsTh;
595 4529049 : if (eps <= epsTh) {
596 : return 0.;
597 4398330 : } else if ((eps > epsTh) && (eps < wTh)) {
598 1166849 : return (eps - epsTh) / w;
599 3231481 : } else if (eps >= wTh) {
600 : return 1.;
601 : } else {
602 0 : throw std::runtime_error("error in function Ef");
603 : }
604 : }
605 :
606 3508137 : double PhotoPionProduction::breitwigner(double sigma0, double gamma, double DMM, double epsPrime, bool onProton) const {
607 3508137 : const double m = mass(onProton);
608 3508137 : const double s = m * m + 2. * m * epsPrime;
609 3508137 : const double gam2s = gamma * gamma * s;
610 3508137 : return sigma0 * (s / epsPrime / epsPrime) * gam2s / ((s - DMM * DMM) * (s - DMM * DMM) + gam2s);
611 : }
612 :
613 1020912 : double PhotoPionProduction::functs(double s, bool onProton) const {
614 1020912 : const double m = mass(onProton);
615 1020912 : const double factor = s - m * m;
616 1020912 : const double epsPrime = factor / 2. / m;
617 1020912 : const double sigmaPg = crossection(epsPrime, onProton);
618 1020912 : return factor * sigmaPg;
619 : }
620 :
621 5741710 : double PhotoPionProduction::mass(bool onProton) const {
622 5741710 : const double m = onProton ? mass_proton : mass_neutron;
623 5741710 : return m / GeV * c_squared;
624 : }
625 :
626 1148592 : double PhotoPionProduction::sMin() const {
627 1148592 : return 1.1646; // [GeV^2] head-on collision
628 : }
629 :
630 0 : void PhotoPionProduction::setSampleLog(bool b) {
631 0 : sampleLog = b;
632 0 : }
633 :
634 0 : void PhotoPionProduction::setCorrectionFactor(double factor) {
635 0 : correctionFactor = factor;
636 0 : }
637 :
638 1 : ref_ptr<PhotonField> PhotoPionProduction::getPhotonField() const {
639 1 : return photonField;
640 : }
641 :
642 0 : bool PhotoPionProduction::getHavePhotons() const {
643 0 : return havePhotons;
644 : }
645 :
646 0 : bool PhotoPionProduction::getHaveNeutrinos() const {
647 0 : return haveNeutrinos;
648 : }
649 :
650 0 : bool PhotoPionProduction::getHaveElectrons() const {
651 0 : return haveElectrons;
652 : }
653 :
654 0 : bool PhotoPionProduction::getHaveAntiNucleons() const {
655 0 : return haveAntiNucleons;
656 : }
657 :
658 0 : bool PhotoPionProduction::getHaveRedshiftDependence() const {
659 0 : return haveRedshiftDependence;
660 : }
661 :
662 0 : double PhotoPionProduction::getLimit() const {
663 0 : return limit;
664 : }
665 :
666 0 : bool PhotoPionProduction::getSampleLog() const {
667 0 : return sampleLog;
668 : }
669 :
670 1 : double PhotoPionProduction::getCorrectionFactor() const {
671 1 : return correctionFactor;
672 : }
673 :
674 1 : void PhotoPionProduction::setInteractionTag(std::string tag) {
675 1 : interactionTag = tag;
676 1 : }
677 :
678 2 : std::string PhotoPionProduction::getInteractionTag() const {
679 2 : return interactionTag;
680 : }
681 :
682 : } // namespace crpropa
|