LCOV - code coverage report
Current view: top level - src/module - EMInverseComptonScattering.cpp (source / functions) Coverage Total Hit
Test: coverage.info.cleaned Lines: 85.4 % 130 111
Test Date: 2026-07-13 06:03:10 Functions: 72.2 % 18 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 <vector>
       7              : #include <cmath>
       8              : 
       9              : namespace crpropa {
      10              : 
      11              : static const double mec2 = mass_electron * c_squared;
      12              : 
      13            5 : EMInverseComptonScattering::EMInverseComptonScattering(ref_ptr<PhotonField> photonField, bool havePhotons, double thinning, double limit, ref_ptr<Surface> surface) {
      14           10 :         setSurface(surface);
      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              :         
      23           17 :         this->photonField = photonField;
      24           17 :         std::string fname = photonField->getFieldName();
      25           34 :         setDescription("EMInverseComptonScattering: " + fname);
      26              :         
      27           17 :         if (!this->photonField->hasPositionDependence()) {
      28              :                 this->interactionRates = new InteractionRatesHomogeneous(
      29           51 :                         getDataPath("EMInverseComptonScattering/rate_" + fname + ".txt"),
      30           51 :                         getDataPath("EMInverseComptonScattering/cdf_" + fname + ".txt")
      31           34 :                 );
      32              :         } else {
      33              :                 this->interactionRates = new InteractionRatesPositionDependent(
      34            0 :                         getDataPath("EMInverseComptonScattering/" + fname + "/Rate/"),
      35            0 :                         getDataPath("EMInverseComptonScattering/" + fname + "/CumulativeRate/"),
      36              :                         this->surface
      37            0 :                 );
      38              :         }
      39           17 : }
      40              : 
      41            6 : void EMInverseComptonScattering::setHavePhotons(bool havePhotons) {
      42            6 :         this->havePhotons = havePhotons;
      43            6 : }
      44              : 
      45            5 : void EMInverseComptonScattering::setLimit(double limit) {
      46            5 :         this->limit = limit;
      47            5 : }
      48              : 
      49            5 : void EMInverseComptonScattering::setThinning(double thinning) {
      50            5 :         this->thinning = thinning;
      51            5 : }
      52              : 
      53            5 : void EMInverseComptonScattering::setSurface(ref_ptr<Surface> surface) {
      54            5 :         this->surface = surface;
      55            5 : }
      56              : 
      57            0 : ref_ptr<Surface> EMInverseComptonScattering::getSurface() const {
      58            0 :         return this->surface;
      59              : }
      60              : 
      61            0 : void EMInverseComptonScattering::setInteractionRates(ref_ptr<InteractionRates> intRates) {
      62            0 :         this->interactionRates = intRates;
      63            0 : }
      64              : 
      65            0 : ref_ptr<InteractionRates> EMInverseComptonScattering::getInteractionRates() const {
      66            0 :         return this->interactionRates;
      67              : }
      68              : 
      69            0 : void EMInverseComptonScattering::initRate(std::string path) {
      70            0 :         this->interactionRates->initRate(path);
      71            0 : }
      72              : 
      73            0 : void EMInverseComptonScattering::initCumulativeRate(std::string path) {
      74            0 :         this->interactionRates->initCumulativeRate(path);
      75            0 : }
      76              : 
      77              : // Class to calculate the energy distribution of the ICS photon and to sample from it
      78              : class ICSSecondariesEnergyDistribution {
      79              :         private:
      80              :                 std::vector< std::vector<double> > data;
      81              :                 std::vector<double> s_values;
      82              :                 size_t Ns;
      83              :                 size_t Nrer;
      84              :                 double s_min;
      85              :                 double s_max;
      86              :                 double dls;
      87              : 
      88              :         public:
      89              :                 // differential cross-section, see Lee '96 (arXiv:9604098), eq. 23 for x = Ee'/Ee
      90              :                 double dSigmadE(double x, double beta) {
      91      1000000 :                         double q = ((1 - beta) / beta) * (1 - 1./x);
      92      1000000 :                         return ((1 + beta) / beta) * (x + 1./x + 2 * q + q * q);
      93              :                 }
      94              : 
      95              :                 // create the cumulative energy distribution of the up-scattered photon
      96            1 :                 ICSSecondariesEnergyDistribution() {
      97            1 :                         Ns = 1000;
      98            1 :                         Nrer = 1000;
      99            1 :                         s_min = mec2 * mec2;
     100            1 :                         s_max = 2e23 * eV * eV;
     101            1 :                         dls = (log(s_max) - log(s_min)) / Ns;
     102            1 :                         data = std::vector< std::vector<double> >(1000, std::vector<double>(1000));
     103            1 :                         std::vector<double> data_i(1000);
     104              : 
     105              :                         // tabulate s bin borders
     106            1 :                         s_values = std::vector<double>(1001);
     107         1002 :                         for (size_t i = 0; i < Ns + 1; ++i)
     108         1001 :                                 s_values[i] = s_min * exp(i*dls);
     109              : 
     110              : 
     111              :                         // for each s tabulate cumulative differential cross section
     112         1001 :                         for (size_t i = 0; i < Ns; i++) {
     113         1000 :                                 double s = s_min * exp((i+0.5) * dls);
     114         1000 :                                 double beta = (s - s_min) / (s + s_min);
     115         1000 :                                 double x0 = (1 - beta) / (1 + beta);
     116         1000 :                                 double dlx = -log(x0) / Nrer;
     117              : 
     118              :                                 // cumulative midpoint integration
     119         1000 :                                 data_i[0] = dSigmadE(x0, beta) * expm1(dlx);
     120      1000000 :                                 for (size_t j = 1; j < Nrer; j++) {
     121       999000 :                                         double x = x0 * exp((j+0.5) * dlx);
     122       999000 :                                         double dx = exp((j+1) * dlx) - exp(j * dlx);
     123       999000 :                                         data_i[j] = dSigmadE(x, beta) * dx;
     124       999000 :                                         data_i[j] += data_i[j-1];
     125              :                                 }
     126         1000 :                                 data[i] = data_i;
     127              :                         }
     128            1 :                 }
     129              : 
     130              :                 // draw random energy for the up-scattered photon Ep(Ee, s)
     131            1 :                 double sample(double Ee, double s) {
     132            1 :                         size_t idx = std::lower_bound(s_values.begin(), s_values.end(), s) - s_values.begin();
     133            1 :                         std::vector<double> s0 = data[idx];
     134            1 :                         Random &random = Random::instance();
     135            1 :                         size_t j = random.randBin(s0) + 1; // draw random bin (upper bin boundary returned)
     136            1 :                         double beta = (s - s_min) / (s + s_min);
     137            1 :                         double x0 = (1 - beta) / (1 + beta);
     138            1 :                         double dlx = -log(x0) / Nrer;
     139            1 :                         double binWidth = x0 * (exp(j * dlx) - exp((j-1) * dlx));
     140            1 :                         double Ep = (x0 * exp((j-1) * dlx) + binWidth) * Ee;
     141            1 :                         return std::min(Ee, Ep); // prevent Ep > Ee from numerical inaccuracies
     142            1 :                 }
     143              : };
     144              : 
     145            1 : double EMInverseComptonScattering::getRate(double E, const Vector3d &position, double z) const {
     146            1 :         return this->interactionRates->getProcessRate(E, position) * pow_integer<2>(1 + z) * photonField->getRedshiftScaling(z);
     147              : }
     148              : 
     149            1 : void EMInverseComptonScattering::performInteraction(Candidate *candidate) const {
     150              : 
     151              :         // scale the particle energy instead of background photons
     152            1 :         double z = candidate->getRedshift();
     153            1 :         double E = candidate->current.getEnergy() * (1 + z);
     154              :         
     155            1 :         Vector3d position = candidate->current.getPosition();
     156              :         
     157              :         std::vector<double> tabE;
     158              :         std::vector<double> tabs;
     159              :         std::vector<std::vector<double>> tabCDF;
     160              :         
     161            1 :         this->interactionRates->loadPerformInteractionTabs(position, tabE, tabs, tabCDF);
     162              :         
     163            1 :         if (E < tabE.front() or E > tabE.back())
     164              :                 return;
     165              :         
     166              :         // sample the value of s
     167            1 :         Random &random = Random::instance();
     168            1 :         size_t i = closestIndex(E, tabE);
     169            1 :         size_t j = random.randBin(tabCDF[i]);
     170            1 :         double s_kin = pow(10, log10(tabs[j]) + (random.rand() - 0.5) * 0.1);
     171            1 :         double s = s_kin + mec2 * mec2;
     172              :         
     173              :         // sample electron energy after scattering
     174            1 :         static ICSSecondariesEnergyDistribution distribution;
     175            1 :         double Enew = distribution.sample(E, s);
     176              :         
     177              :         // add up-scattered photon
     178            1 :         double Esecondary = E - Enew;
     179            1 :         double f = Enew / E;
     180            1 :         if (havePhotons) {
     181            1 :                 if (random.rand() < pow(1 - f, thinning)) {
     182            1 :                         double w = 1. / pow(1 - f, thinning);
     183            1 :                         Vector3d pos = random.randomInterpolatedPosition(candidate->previous.getPosition(), candidate->current.getPosition());
     184            1 :                         candidate->addSecondary(22, Esecondary / (1 + z), pos, w, interactionTag);
     185              :                 }
     186              :         }
     187              :         
     188              :         // update the primary particle energy; do this after adding the secondary to correctly set the secondary's parent
     189            1 :         candidate->current.setEnergy(Enew / (1 + z));
     190            1 : }
     191              : 
     192         1307 : void EMInverseComptonScattering::process(Candidate *candidate) const {
     193              :         // check if electron / positron
     194         1307 :         int id = candidate->current.getId();
     195         1307 :         if (abs(id) != 11)
     196         1307 :                 return;
     197              :         
     198              :         // scale the particle energy instead of background photons
     199            1 :         double z = candidate->getRedshift();
     200            1 :         double E = candidate->current.getEnergy() * (1 + z);
     201            1 :         Vector3d position = candidate->current.getPosition();
     202              :         
     203              :         // interaction rate
     204            1 :         double rate = getRate(E, position, z);
     205            1 :         if (rate < 0)
     206              :                 return;
     207              :         
     208            1 :         rate *= pow_integer<2>(1 + z) * photonField->getRedshiftScaling(z);
     209              :         
     210              :         // run this loop at least once to limit the step size
     211            1 :         double step = candidate->getCurrentStep();
     212            1 :         Random &random = Random::instance();
     213              :         do {
     214            1 :                 double randDistance = -log(random.rand()) / rate;
     215              :                 
     216              :                 // check for interaction; if it doesn't ocurr, limit next step
     217            1 :                 if (step < randDistance) {
     218            1 :                         candidate->limitNextStep(limit / rate);
     219            1 :                         return;
     220              :                 }
     221            0 :                 performInteraction(candidate);
     222              :                 
     223              :                 // repeat with remaining step
     224            0 :                 step -= randDistance;
     225            0 :         } while (step > 0);
     226              : }
     227              : 
     228            1 : void EMInverseComptonScattering::setInteractionTag(std::string tag) {
     229            1 :         interactionTag = tag;
     230            1 : }
     231              : 
     232            2 : std::string EMInverseComptonScattering::getInteractionTag() const {
     233            2 :         return interactionTag;
     234              : }
     235              : 
     236              : } // namespace crpropa
        

Generated by: LCOV version 2.0-1