LCOV - code coverage report
Current view: top level - src/module - ElectronPairProduction.cpp (source / functions) Coverage Total Hit
Test: coverage.info.cleaned Lines: 92.2 % 90 83
Test Date: 2026-06-18 09:49:19 Functions: 90.0 % 10 9

            Line data    Source code
       1              : #include "crpropa/module/ElectronPairProduction.h"
       2              : #include "crpropa/Units.h"
       3              : #include "crpropa/ParticleID.h"
       4              : #include "crpropa/ParticleMass.h"
       5              : #include "crpropa/Random.h"
       6              : 
       7              : #include <fstream>
       8              : #include <limits>
       9              : #include <stdexcept>
      10              : 
      11              : namespace crpropa {
      12              : 
      13           10 : ElectronPairProduction::ElectronPairProduction(ref_ptr<PhotonField> photonField,
      14           10 :                 bool haveElectrons, double limit) {
      15           10 :         this->haveElectrons = haveElectrons;
      16           10 :         this->limit = limit;
      17           10 :         setPhotonField(photonField);
      18           10 : }
      19              : 
      20           19 : void ElectronPairProduction::setPhotonField(ref_ptr<PhotonField> photonField) {
      21           19 :         this->photonField = photonField;
      22           19 :         std::string fname = photonField->getFieldName();
      23           19 :         setDescription("ElectronPairProduction: " + fname);
      24           38 :         initRate(getDataPath("ElectronPairProduction/lossrate_" + fname + ".txt"));
      25           19 :         if (haveElectrons) { // Load secondary spectra only if electrons should be produced
      26            0 :                 initSpectrum(getDataPath("ElectronPairProduction/spectrum_" + fname.substr(0,3) + ".txt"));
      27              :         }
      28           19 : }
      29              : 
      30            1 : void ElectronPairProduction::setHaveElectrons(bool haveElectrons) {
      31            1 :         this->haveElectrons = haveElectrons;
      32            1 :         if (haveElectrons) { // Load secondary spectra in case haveElectrons was changed to true
      33            1 :                 std::string fname = photonField->getFieldName();
      34            3 :                 initSpectrum(getDataPath("ElectronPairProduction/spectrum_" + fname.substr(0,3) + ".txt"));
      35              :         }
      36            1 : }
      37              : 
      38            0 : void ElectronPairProduction::setLimit(double limit) {
      39            0 :         this->limit = limit;
      40            0 : }
      41              : 
      42           19 : void ElectronPairProduction::initRate(std::string filename) {
      43           19 :         std::ifstream infile(filename.c_str());
      44              : 
      45           19 :         if (!infile.good())
      46            0 :                 throw std::runtime_error("ElectronPairProduction: could not open file " + filename);
      47              : 
      48              :         // clear previously loaded interaction rates
      49              :         tabLorentzFactor.clear();
      50              :         tabLossRate.clear();
      51              : 
      52         2853 :         while (infile.good()) {
      53         2834 :                 if (infile.peek() != '#') {
      54              :                         double a, b;
      55              :                         infile >> a >> b;
      56         2777 :                         if (infile) {
      57         2758 :                                 tabLorentzFactor.push_back(pow(10, a));
      58         2758 :                                 tabLossRate.push_back(b / Mpc);
      59              :                         }
      60              :                 }
      61         2834 :                 infile.ignore(std::numeric_limits < std::streamsize > ::max(), '\n');
      62              :         }
      63           19 :         infile.close();
      64           19 : }
      65              : 
      66            1 : void ElectronPairProduction::initSpectrum(std::string filename) {
      67            1 :         std::ifstream infile(filename.c_str());
      68            1 :         if (!infile.good())
      69            0 :                 throw std::runtime_error("ElectronPairProduction: could not open file " + filename);
      70              : 
      71              :         double dNdE;
      72            1 :         tabSpectrum.resize(70);
      73           71 :         for (size_t i = 0; i < 70; i++) {
      74           70 :                 tabSpectrum[i].resize(170);
      75        11970 :                 for (size_t j = 0; j < 170; j++) {
      76              :                         infile >> dNdE;
      77        11900 :                         tabSpectrum[i][j] = dNdE * pow(10, (7 + 0.1 * j)); // read electron distribution pdf(Ee) ~ dN/dEe * Ee
      78              :                 }
      79        11900 :                 for (size_t j = 1; j < 170; j++) {
      80        11830 :                         tabSpectrum[i][j] += tabSpectrum[i][j - 1]; // cdf(Ee), unnormalized
      81              :                 }
      82              :         }
      83            1 :         infile.close();
      84            1 : }
      85              : 
      86         1030 : double ElectronPairProduction::lossLength(int id, double lf, double z) const {
      87         1030 :         double Z = chargeNumber(id);
      88         1030 :         if (Z == 0)
      89              :                 return std::numeric_limits<double>::max(); // no pair production on uncharged particles
      90              : 
      91         1018 :         lf *= (1 + z);
      92         1018 :         if (lf < tabLorentzFactor.front())
      93              :                 return std::numeric_limits<double>::max(); // below energy threshold
      94              : 
      95              :         double rate;
      96          995 :         if (lf < tabLorentzFactor.back())
      97          995 :                 rate = interpolate(lf, tabLorentzFactor, tabLossRate); // interpolation
      98              :         else
      99            0 :                 rate = tabLossRate.back() * pow(lf / tabLorentzFactor.back(), -0.6); // extrapolation
     100              : 
     101          995 :         double A = nuclearMass(id) / mass_proton; // more accurate than massNumber(Id)
     102          995 :         rate *= Z * Z / A * pow_integer<3>(1 + z) * photonField->getRedshiftScaling(z);
     103          995 :         return 1. / rate;
     104              : }
     105              : 
     106         1031 : void ElectronPairProduction::process(Candidate *c) const {
     107         1031 :         int id = c->current.getId();
     108         1031 :         if (not (isNucleus(id)))
     109              :                 return; // only nuclei
     110              : 
     111         1030 :         double lf = c->current.getLorentzFactor();
     112         1030 :         double z = c->getRedshift();
     113         1030 :         double losslen = lossLength(id, lf, z);  // energy loss length
     114         1030 :         if (losslen >= std::numeric_limits<double>::max())
     115              :                 return;
     116              : 
     117          995 :         double step = c->getCurrentStep() / (1 + z); // step size in local frame
     118          995 :         double loss = step / losslen;  // relative energy loss
     119              : 
     120          995 :         if (haveElectrons) {
     121            1 :                 double dE = c->current.getEnergy() * loss;  // energy loss
     122            1 :                 int i = round((log10(lf) - 6.05) * 10);  // find closest cdf(Ee|log10(gamma))
     123            1 :                 i = std::min(std::max(i, 0), 69);
     124            1 :                 Random &random = Random::instance();
     125              : 
     126              :                 // draw pairs as long as their energy is smaller than the pair production energy loss
     127         6173 :                 while (dE > 0) {
     128         6172 :                         size_t j = random.randBin(tabSpectrum[i]);
     129         6172 :                         double Ee = pow(10, 6.95 + (j + random.rand()) * 0.1) * eV;
     130         6172 :                         double Epair = 2 * Ee; // NOTE: electron and positron in general don't have same lab frame energy, but averaged over many draws the result is consistent
     131              :                         // if the remaining energy is not sufficient check for random accepting
     132         6172 :                         if (Epair > dE)
     133            1 :                                 if (random.rand() > (dE / Epair))
     134              :                                         break; // not accepted
     135              : 
     136              :                         // create pair and repeat with remaining energy
     137         6172 :                         dE -= Epair;
     138         6172 :                         Vector3d pos = random.randomInterpolatedPosition(c->previous.getPosition(), c->current.getPosition());
     139         6172 :                         c->addSecondary( 11, Ee, pos, 1., interactionTag);
     140         6172 :                         c->addSecondary(-11, Ee, pos, 1., interactionTag);
     141              :                 }
     142              :         }
     143              : 
     144          995 :         c->current.setLorentzFactor(lf * (1 - loss));
     145          995 :         c->limitNextStep(limit * losslen);
     146              : }
     147              : 
     148            1 : void ElectronPairProduction::setInteractionTag(std::string tag) {
     149            1 :         interactionTag = tag;
     150            1 : }
     151              : 
     152            2 : std::string ElectronPairProduction::getInteractionTag() const {
     153            2 :         return interactionTag;
     154              : }
     155              : 
     156              : 
     157              : } // namespace crpropa
        

Generated by: LCOV version 2.0-1