LCOV - code coverage report
Current view: top level - src/module - SynchrotronRadiation.cpp (source / functions) Coverage Total Hit
Test: coverage.info.cleaned Lines: 86.4 % 132 114
Test Date: 2026-06-18 09:49:19 Functions: 95.2 % 21 20

            Line data    Source code
       1              : #include "crpropa/module/SynchrotronRadiation.h"
       2              : #include "crpropa/Units.h"
       3              : #include "crpropa/Random.h"
       4              : 
       5              : #include <fstream>
       6              : #include <limits>
       7              : #include <stdexcept>
       8              : 
       9              : namespace crpropa {
      10              : 
      11            2 : SynchrotronRadiation::SynchrotronRadiation(ref_ptr<MagneticField> field, bool havePhotons, double thinning, int nSamples, double limit) {
      12            2 :         setField(field);
      13            2 :         setBrms(0);
      14            2 :         initSpectrum();
      15            2 :         setHavePhotons(havePhotons);
      16            2 :         setLimit(limit);
      17            2 :         setSecondaryThreshold(1e6 * eV);
      18            2 :         setMaximumSamples(nSamples);
      19            2 :         setThinning(thinning);
      20            2 : }
      21              : 
      22            6 : SynchrotronRadiation::SynchrotronRadiation(double Brms, bool havePhotons, double thinning, int nSamples, double limit) {
      23            6 :         setBrms(Brms);
      24            6 :         initSpectrum();
      25            6 :         setHavePhotons(havePhotons);
      26            6 :         setLimit(limit);
      27            6 :         setSecondaryThreshold(1e6 * eV);
      28            6 :         setMaximumSamples(nSamples);
      29            6 :         setThinning(thinning);
      30            6 : }
      31              : 
      32            3 : void SynchrotronRadiation::setField(ref_ptr<MagneticField> f) {
      33            3 :         this->field = f;
      34            3 : }
      35              : 
      36            3 : ref_ptr<MagneticField> SynchrotronRadiation::getField() {
      37            3 :         return field;
      38              : }
      39              : 
      40            9 : void SynchrotronRadiation::setBrms(double Brms) {
      41            9 :         this->Brms = Brms;
      42            9 : }
      43              : 
      44            5 : double SynchrotronRadiation::getBrms() {
      45            5 :         return Brms;
      46              : }
      47              : 
      48            9 : void SynchrotronRadiation::setHavePhotons(bool havePhotons) {
      49            9 :         this->havePhotons = havePhotons;
      50            9 : }
      51              : 
      52            5 : bool SynchrotronRadiation::getHavePhotons() {
      53            5 :         return havePhotons;
      54              : }
      55              : 
      56            9 : void SynchrotronRadiation::setThinning(double thinning) {
      57            9 :         this->thinning = thinning;
      58            9 : }
      59              : 
      60            5 : double SynchrotronRadiation::getThinning() {
      61            5 :         return thinning;
      62              : }
      63              : 
      64            9 : void SynchrotronRadiation::setLimit(double limit) {
      65            9 :         this->limit = limit;
      66            9 : }
      67              : 
      68            5 : double SynchrotronRadiation::getLimit() {
      69            5 :         return limit;
      70              : }
      71              : 
      72           10 : void SynchrotronRadiation::setMaximumSamples(int nmax) {
      73           10 :         maximumSamples = nmax;
      74           10 : }
      75              : 
      76            5 : int SynchrotronRadiation::getMaximumSamples() {
      77            5 :         return maximumSamples;
      78              : }
      79              : 
      80           10 : void SynchrotronRadiation::setSecondaryThreshold(double threshold) {
      81           10 :         secondaryThreshold = threshold;
      82           10 : }
      83              : 
      84            5 : double SynchrotronRadiation::getSecondaryThreshold() const {
      85            5 :         return secondaryThreshold;
      86              : }
      87              : 
      88            8 : void SynchrotronRadiation::initSpectrum() {
      89           16 :         std::string filename = getDataPath("Synchrotron/spectrum.txt");
      90            8 :         std::ifstream infile(filename.c_str());
      91              : 
      92            8 :         if (!infile.good())
      93            0 :                 throw std::runtime_error("SynchrotronRadiation: could not open file " + filename);
      94              : 
      95              :         // clear previously loaded interaction rates
      96              :         tabx.clear();
      97              :         tabCDF.clear();
      98              : 
      99        11256 :         while (infile.good()) {
     100        11248 :                 if (infile.peek() != '#') {
     101              :                         double a, b;
     102              :                         infile >> a >> b;
     103        11216 :                         if (infile) {
     104        11208 :                                 tabx.push_back(pow(10, a));
     105        11208 :                                 tabCDF.push_back(b);
     106              :                         }
     107              :                 }
     108        11248 :                 infile.ignore(std::numeric_limits < std::streamsize > ::max(), '\n');
     109              :         }
     110            8 :         infile.close();
     111           16 : }
     112              : 
     113            6 : void SynchrotronRadiation::process(Candidate *candidate) const {
     114            6 :         double charge = fabs(candidate->current.getCharge());
     115            6 :         if (charge == 0)
     116            4 :                 return; // only charged particles
     117              : 
     118              :         // calculate gyroradius, evaluated at the current position
     119            6 :         double z = candidate->getRedshift();
     120              :         double B;
     121            6 :         if (field.valid()) {
     122            0 :                 Vector3d Bvec = field->getField(candidate->current.getPosition(), z);
     123            0 :                 B = Bvec.cross(candidate->current.getDirection()).getR();
     124              :         } else {
     125            6 :                 B = sqrt(2. / 3) * Brms; // average perpendicular field component
     126              :         }
     127            6 :         B *= pow(1 + z, 2); // cosmological scaling
     128            6 :         double Rg = candidate->current.getMomentum().getR() / charge / B;
     129              : 
     130              :         // calculate energy loss
     131            6 :         double lf = candidate->current.getLorentzFactor();
     132            6 :         double dEdx = 1. / 6 / M_PI / epsilon0 * pow(lf * lf - 1, 2) * pow(charge / Rg, 2); // Jackson p. 770 (14.31)
     133            6 :         double step = candidate->getCurrentStep() / (1 + z); // step size in local frame
     134            6 :         double dE = step * dEdx;
     135              : 
     136              :         // apply energy loss and limit next step
     137            6 :         double E = candidate->current.getEnergy();
     138            6 :         candidate->current.setEnergy(E - dE);
     139            6 :         candidate->limitNextStep(limit * E / dEdx);
     140              : 
     141              :         // optionally add secondary photons
     142            6 :         if (not(havePhotons))
     143              :                 return;
     144              : 
     145              :         // check if photons with energies > 14 * Ecrit are possible
     146            2 :         double Ecrit = 3. / 4 * h_planck / M_PI * c_light * pow(lf, 3) / Rg;
     147            2 :         if (14 * Ecrit < secondaryThreshold)
     148              :                 return;
     149              : 
     150              :         // draw photons up to the total energy loss
     151              :         // if maximumSamples is reached before that, compensate the total energy afterwards
     152            2 :         Random &random = Random::instance();
     153              :         double dE0 = dE;
     154              :         std::vector<double> energies;
     155              :         int counter = 0;
     156      3626394 :         while (dE > 0) {
     157              :                 // draw random value between 0 and maximum of corresponding cdf
     158              :                 // choose bin of s where cdf(x) = cdf_rand -> x_rand
     159      3626394 :                 size_t i = random.randBin(tabCDF); // draw random bin (upper bin boundary returned)
     160      3626394 :                 double binWidth = (tabx[i] - tabx[i-1]);
     161      3626394 :                 double x = tabx[i-1] + random.rand() * binWidth; // draw random x uniformly distributed in bin
     162      3626394 :                 double Ephoton = x * Ecrit;
     163              : 
     164              :                 // if the remaining energy is not sufficient check for random accepting
     165      3626394 :                 if (Ephoton > dE) {
     166            1 :                         if (random.rand() > (dE / Ephoton))
     167              :                                 break; // not accepted
     168              :                 }
     169              : 
     170              :                 // only activate the "per-step" sampling if maximumSamples is explicitly set.
     171      3626393 :                 if (maximumSamples > 0) {
     172         1001 :                         if (counter >= maximumSamples) 
     173              :                                 break;                  
     174              :                 }
     175              : 
     176              :                 // store energies in array
     177      3626392 :                 energies.push_back(Ephoton);
     178              : 
     179              :                 // energy loss
     180      3626392 :                 dE -= Ephoton;
     181              : 
     182              :                 // counter for sampling break condition;
     183      3626392 :                 counter++;
     184              :         }
     185              : 
     186              :         // while loop before gave total energy which is just a fraction of the required
     187              :         double w1 = 1;
     188            2 :         if (maximumSamples > 0 && dE > 0)
     189            1 :                 w1 = 1. / (1. - dE / dE0); 
     190              : 
     191              :         // loop over sampled photons and attribute weights accordingly
     192      3626394 :         for (int i = 0; i < energies.size(); i++) {
     193      3626392 :                 double Ephoton = energies[i];
     194      3626392 :                 double f = Ephoton / (E - dE0);
     195      3626392 :                 double w = w1 / pow(f, thinning);
     196              : 
     197              :                 // thinning procedure: accepts only a few random secondaries
     198      3626392 :                 if (random.rand() < pow(f, thinning)) {
     199      3626392 :                         Vector3d pos = random.randomInterpolatedPosition(candidate->previous.getPosition(), candidate->current.getPosition());
     200      3626392 :                         if (Ephoton > secondaryThreshold) // create only photons with energies above threshold
     201      3314271 :                                 candidate->addSecondary(22, Ephoton, pos, w, interactionTag);
     202              :                 }
     203              :         }
     204            2 : }
     205              : 
     206            0 : std::string SynchrotronRadiation::getDescription() const {
     207            0 :         std::stringstream s;
     208            0 :         s << "Synchrotron radiation";
     209            0 :         if (field.valid())
     210            0 :                 s << " for specified magnetic field";
     211              :         else
     212            0 :                 s << " for Brms = " << Brms / nG << " nG";
     213            0 :         if (havePhotons)
     214            0 :                 s << ", synchrotron photons E > " << secondaryThreshold / eV << " eV";
     215              :         else
     216            0 :                 s << ", no synchrotron photons";
     217            0 :         if (maximumSamples > 0)
     218            0 :                 s << "maximum number of photon samples: " << maximumSamples;
     219            0 :         if (thinning > 0)
     220            0 :                 s << "thinning parameter: " << thinning; 
     221            0 :         return s.str();
     222            0 : }
     223              : 
     224            1 : void SynchrotronRadiation::setInteractionTag(std::string tag) {
     225            1 :         interactionTag = tag;
     226            1 : }
     227              : 
     228            2 : std::string SynchrotronRadiation::getInteractionTag() const {
     229            2 :         return interactionTag;
     230              : }
     231              : 
     232              : } // namespace crpropa
        

Generated by: LCOV version 2.0-1