LCOV - code coverage report
Current view: top level - src/module - EMInverseComptonScattering.cpp (source / functions) Coverage Total Hit
Test: coverage.info.cleaned Lines: 96.2 % 130 125
Test Date: 2026-06-18 09:49:19 Functions: 100.0 % 13 13

            Line data    Source code
       1              : #include "crpropa/module/EMInverseComptonScattering.h"
       2              : #include "crpropa/Units.h"
       3              : #include "crpropa/Random.h"
       4              : #include "crpropa/Common.h"
       5              : 
       6              : #include <fstream>
       7              : #include <limits>
       8              : #include <stdexcept>
       9              : 
      10              : namespace crpropa {
      11              : 
      12              : static const double mec2 = mass_electron * c_squared;
      13              : 
      14            5 : EMInverseComptonScattering::EMInverseComptonScattering(ref_ptr<PhotonField> photonField, bool havePhotons, double thinning, double limit) {
      15            5 :         setPhotonField(photonField);
      16            5 :         setHavePhotons(havePhotons);
      17            5 :         setLimit(limit);
      18            5 :         setThinning(thinning);
      19            5 : }
      20              : 
      21           17 : void EMInverseComptonScattering::setPhotonField(ref_ptr<PhotonField> photonField) {
      22           17 :         this->photonField = photonField;
      23           17 :         std::string fname = photonField->getFieldName();
      24           17 :         setDescription("EMInverseComptonScattering: " + fname);
      25           34 :         initRate(getDataPath("EMInverseComptonScattering/rate_" + fname + ".txt"));
      26           51 :         initCumulativeRate(getDataPath("EMInverseComptonScattering/cdf_" + fname + ".txt"));
      27           17 : }
      28              : 
      29            6 : void EMInverseComptonScattering::setHavePhotons(bool havePhotons) {
      30            6 :         this->havePhotons = havePhotons;
      31            6 : }
      32              : 
      33            5 : void EMInverseComptonScattering::setLimit(double limit) {
      34            5 :         this->limit = limit;
      35            5 : }
      36              : 
      37            5 : void EMInverseComptonScattering::setThinning(double thinning) {
      38            5 :         this->thinning = thinning;
      39            5 : }
      40              : 
      41           17 : void EMInverseComptonScattering::initRate(std::string filename) {
      42           17 :         std::ifstream infile(filename.c_str());
      43              : 
      44           17 :         if (!infile.good())
      45            0 :                 throw std::runtime_error("EMInverseComptonScattering: could not open file " + filename);
      46              : 
      47              :         // clear previously loaded tables
      48              :         tabEnergy.clear();
      49              :         tabRate.clear();
      50              : 
      51         4879 :         while (infile.good()) {
      52         4862 :                 if (infile.peek() != '#') {
      53              :                         double a, b;
      54              :                         infile >> a >> b;
      55         4794 :                         if (infile) {
      56         4777 :                                 tabEnergy.push_back(pow(10, a) * eV);
      57         4777 :                                 tabRate.push_back(b / Mpc);
      58              :                         }
      59              :                 }
      60         4862 :                 infile.ignore(std::numeric_limits < std::streamsize > ::max(), '\n');
      61              :         }
      62           17 :         infile.close();
      63           17 : }
      64              : 
      65           17 : void EMInverseComptonScattering::initCumulativeRate(std::string filename) {
      66           17 :         std::ifstream infile(filename.c_str());
      67              : 
      68           17 :         if (!infile.good())
      69            0 :                 throw std::runtime_error("EMInverseComptonScattering: could not open file " + filename);
      70              : 
      71              :         // clear previously loaded tables
      72              :         tabE.clear();
      73              :         tabs.clear();
      74              :         tabCDF.clear();
      75              :         
      76              :         // skip header
      77           85 :         while (infile.peek() == '#')
      78           68 :                 infile.ignore(std::numeric_limits < std::streamsize > ::max(), '\n');
      79              : 
      80              :         // read s values in first line
      81              :         double a;
      82              :         infile >> a; // skip first value
      83         3000 :         while (infile.good() and (infile.peek() != '\n')) {
      84              :                 infile >> a;
      85         2983 :                 tabs.push_back(pow(10, a) * eV * eV);
      86              :         }
      87              : 
      88              :         // read all following lines: E, cdf values
      89         4794 :         while (infile.good()) {
      90              :                 infile >> a;
      91         4794 :                 if (!infile)
      92              :                         break;  // end of file
      93         4777 :                 tabE.push_back(pow(10, a) * eV);
      94              :                 std::vector<double> cdf;
      95       843000 :                 for (int i = 0; i < tabs.size(); i++) {
      96              :                         infile >> a;
      97       838223 :                         cdf.push_back(a / Mpc);
      98              :                 }
      99         4777 :                 tabCDF.push_back(cdf);
     100         4777 :         }
     101           17 :         infile.close();
     102           17 : }
     103              : 
     104              : // Class to calculate the energy distribution of the ICS photon and to sample from it
     105              : class ICSSecondariesEnergyDistribution {
     106              :         private:
     107              :                 std::vector< std::vector<double> > data;
     108              :                 std::vector<double> s_values;
     109              :                 size_t Ns;
     110              :                 size_t Nrer;
     111              :                 double s_min;
     112              :                 double s_max;
     113              :                 double dls;
     114              : 
     115              :         public:
     116              :                 // differential cross-section, see Lee '96 (arXiv:9604098), eq. 23 for x = Ee'/Ee
     117              :                 double dSigmadE(double x, double beta) {
     118      1000000 :                         double q = ((1 - beta) / beta) * (1 - 1./x);
     119      1000000 :                         return ((1 + beta) / beta) * (x + 1./x + 2 * q + q * q);
     120              :                 }
     121              : 
     122              :                 // create the cumulative energy distribution of the up-scattered photon
     123            1 :                 ICSSecondariesEnergyDistribution() {
     124            1 :                         Ns = 1000;
     125            1 :                         Nrer = 1000;
     126            1 :                         s_min = mec2 * mec2;
     127            1 :                         s_max = 2e23 * eV * eV;
     128            1 :                         dls = (log(s_max) - log(s_min)) / Ns;
     129            1 :                         data = std::vector< std::vector<double> >(1000, std::vector<double>(1000));
     130            1 :                         std::vector<double> data_i(1000);
     131              : 
     132              :                         // tabulate s bin borders
     133            1 :                         s_values = std::vector<double>(1001);
     134         1002 :                         for (size_t i = 0; i < Ns + 1; ++i)
     135         1001 :                                 s_values[i] = s_min * exp(i*dls);
     136              : 
     137              : 
     138              :                         // for each s tabulate cumulative differential cross section
     139         1001 :                         for (size_t i = 0; i < Ns; i++) {
     140         1000 :                                 double s = s_min * exp((i+0.5) * dls);
     141         1000 :                                 double beta = (s - s_min) / (s + s_min);
     142         1000 :                                 double x0 = (1 - beta) / (1 + beta);
     143         1000 :                                 double dlx = -log(x0) / Nrer;
     144              : 
     145              :                                 // cumulative midpoint integration
     146         1000 :                                 data_i[0] = dSigmadE(x0, beta) * expm1(dlx);
     147      1000000 :                                 for (size_t j = 1; j < Nrer; j++) {
     148       999000 :                                         double x = x0 * exp((j+0.5) * dlx);
     149       999000 :                                         double dx = exp((j+1) * dlx) - exp(j * dlx);
     150       999000 :                                         data_i[j] = dSigmadE(x, beta) * dx;
     151       999000 :                                         data_i[j] += data_i[j-1];
     152              :                                 }
     153         1000 :                                 data[i] = data_i;
     154              :                         }
     155            1 :                 }
     156              : 
     157              :                 // draw random energy for the up-scattered photon Ep(Ee, s)
     158            1 :                 double sample(double Ee, double s) {
     159            1 :                         size_t idx = std::lower_bound(s_values.begin(), s_values.end(), s) - s_values.begin();
     160            1 :                         std::vector<double> s0 = data[idx];
     161            1 :                         Random &random = Random::instance();
     162            1 :                         size_t j = random.randBin(s0) + 1; // draw random bin (upper bin boundary returned)
     163            1 :                         double beta = (s - s_min) / (s + s_min);
     164            1 :                         double x0 = (1 - beta) / (1 + beta);
     165            1 :                         double dlx = -log(x0) / Nrer;
     166            1 :                         double binWidth = x0 * (exp(j * dlx) - exp((j-1) * dlx));
     167            1 :                         double Ep = (x0 * exp((j-1) * dlx) + binWidth) * Ee;
     168            1 :                         return std::min(Ee, Ep); // prevent Ep > Ee from numerical inaccuracies
     169            1 :                 }
     170              : };
     171              : 
     172            1 : void EMInverseComptonScattering::performInteraction(Candidate *candidate) const {
     173              :         // scale the particle energy instead of background photons
     174            1 :         double z = candidate->getRedshift();
     175            1 :         double E = candidate->current.getEnergy() * (1 + z);
     176              : 
     177            1 :         if (E < tabE.front() or E > tabE.back())
     178              :                 return;
     179              : 
     180              :         // sample the value of s
     181            1 :         Random &random = Random::instance();
     182            1 :         size_t i = closestIndex(E, tabE);
     183            1 :         size_t j = random.randBin(tabCDF[i]);
     184            1 :         double s_kin = pow(10, log10(tabs[j]) + (random.rand() - 0.5) * 0.1);
     185            1 :         double s = s_kin + mec2 * mec2;
     186              : 
     187              :         // sample electron energy after scattering
     188            1 :         static ICSSecondariesEnergyDistribution distribution;
     189            1 :         double Enew = distribution.sample(E, s);
     190              : 
     191              :         // add up-scattered photon
     192            1 :         if (havePhotons) {
     193            1 :                 double Esecondary = E - Enew;
     194            1 :                 double f = Enew / E;
     195            1 :                 if (random.rand() < pow(1 - f, thinning)) {
     196            1 :                         double w = 1. / pow(1 - f, thinning);
     197            1 :                         Vector3d pos = random.randomInterpolatedPosition(candidate->previous.getPosition(), candidate->current.getPosition());
     198            1 :                         candidate->addSecondary(22, Esecondary / (1 + z), pos, w, interactionTag);
     199              :                 }
     200              :         }
     201              : 
     202              :         // update the primary particle energy; do this after adding the secondary to correctly set the secondary's parent
     203            1 :         candidate->current.setEnergy(Enew / (1 + z));
     204              : }
     205              : 
     206          869 : void EMInverseComptonScattering::process(Candidate *candidate) const {
     207              :         // check if electron / positron
     208          869 :         int id = candidate->current.getId();
     209          869 :         if (abs(id) != 11)
     210              :                 return;
     211              : 
     212              :         // scale the particle energy instead of background photons
     213            1 :         double z = candidate->getRedshift();
     214            1 :         double E = candidate->current.getEnergy() * (1 + z);
     215              : 
     216            1 :         if (E < tabEnergy.front() or (E > tabEnergy.back()))
     217              :                 return;
     218              : 
     219              :         // interaction rate
     220            1 :         double rate = interpolate(E, tabEnergy, tabRate);
     221            1 :         rate *= pow_integer<2>(1 + z) * photonField->getRedshiftScaling(z);
     222              : 
     223              :         // run this loop at least once to limit the step size
     224            1 :         double step = candidate->getCurrentStep();
     225            1 :         Random &random = Random::instance();
     226              :         do {
     227            1 :                 double randDistance = -log(random.rand()) / rate;
     228              : 
     229              :                 // check for interaction; if it doesn't ocurr, limit next step
     230            1 :                 if (step < randDistance) {
     231            1 :                         candidate->limitNextStep(limit / rate);
     232            1 :                         return;
     233              :                 }
     234            0 :                 performInteraction(candidate);
     235              : 
     236              :                 // repeat with remaining step
     237            0 :                 step -= randDistance;
     238            0 :         } while (step > 0);
     239              : }
     240              : 
     241            1 : void EMInverseComptonScattering::setInteractionTag(std::string tag) {
     242            1 :         interactionTag = tag;
     243            1 : }
     244              : 
     245            2 : std::string EMInverseComptonScattering::getInteractionTag() const {
     246            2 :         return interactionTag;
     247              : }
     248              : 
     249              : } // namespace crpropa
        

Generated by: LCOV version 2.0-1