LCOV - code coverage report
Current view: top level - src/module - PhotoDisintegration.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 140 175 80.0 %
Date: 2024-04-29 14:43:01 Functions: 10 12 83.3 %

          Line data    Source code
       1             : #include "crpropa/module/PhotoDisintegration.h"
       2             : #include "crpropa/Units.h"
       3             : #include "crpropa/ParticleID.h"
       4             : #include "crpropa/ParticleMass.h"
       5             : #include "crpropa/Random.h"
       6             : #include "kiss/logger.h"
       7             : 
       8             : #include <cmath>
       9             : #include <limits>
      10             : #include <sstream>
      11             : #include <fstream>
      12             : #include <stdexcept>
      13             : 
      14             : namespace crpropa {
      15             : 
      16             : const double PhotoDisintegration::lgmin = 6;  // minimum log10(Lorentz-factor)
      17             : const double PhotoDisintegration::lgmax = 14; // maximum log10(Lorentz-factor)
      18             : const size_t PhotoDisintegration::nlg = 201;  // number of Lorentz-factor steps
      19             : 
      20          11 : PhotoDisintegration::PhotoDisintegration(ref_ptr<PhotonField> f, bool havePhotons, double limit) {
      21          11 :         setPhotonField(f);
      22          11 :         this->havePhotons = havePhotons;
      23          11 :         this->limit = limit;
      24          11 : }
      25             : 
      26          22 : void PhotoDisintegration::setPhotonField(ref_ptr<PhotonField> photonField) {
      27          22 :         this->photonField = photonField;
      28          22 :         std::string fname = photonField->getFieldName();
      29          22 :         setDescription("PhotoDisintegration: " + fname);
      30          66 :         initRate(getDataPath("Photodisintegration/rate_" + fname + ".txt"));
      31          66 :         initBranching(getDataPath("Photodisintegration/branching_" + fname + ".txt"));
      32          66 :         initPhotonEmission(getDataPath("Photodisintegration/photon_emission_" + fname.substr(0,3) + ".txt"));
      33          22 : }
      34             : 
      35           1 : void PhotoDisintegration::setHavePhotons(bool havePhotons) {
      36           1 :         this->havePhotons = havePhotons;
      37           1 : }
      38             : 
      39           0 : void PhotoDisintegration::setLimit(double limit) {
      40           0 :         this->limit = limit;
      41           0 : }
      42             : 
      43          22 : void PhotoDisintegration::initRate(std::string filename) {
      44          22 :         std::ifstream infile(filename.c_str());
      45          22 :         if (not infile.good())
      46           0 :                 throw std::runtime_error("PhotoDisintegration: could not open file " + filename);
      47             : 
      48             :         // clear previously loaded interaction rates
      49          22 :         pdRate.clear();
      50          22 :         pdRate.resize(27 * 31);
      51             : 
      52             :         std::string line;
      53        4158 :         while (std::getline(infile, line)) {
      54        4136 :                 if (line[0] == '#')
      55          66 :                         continue;
      56        4070 :                 std::stringstream lineStream(line);
      57             : 
      58             :                 int Z, N;
      59        4070 :                 lineStream >> Z;
      60        4070 :                 lineStream >> N;
      61             : 
      62             :                 double r;
      63      822140 :                 for (size_t i = 0; i < nlg; i++) {
      64             :                         lineStream >> r;
      65      818070 :                         pdRate[Z * 31 + N].push_back(r / Mpc);
      66             :                 }
      67        4070 :         }
      68          22 :         infile.close();
      69          22 : }
      70             : 
      71          22 : void PhotoDisintegration::initBranching(std::string filename) {
      72          22 :         std::ifstream infile(filename.c_str());
      73          22 :         if (not infile.good())
      74           0 :                 throw std::runtime_error("PhotoDisintegration: could not open file " + filename);
      75             : 
      76             :         // clear previously loaded interaction rates
      77          22 :         pdBranch.clear();
      78          22 :         pdBranch.resize(27 * 31);
      79             : 
      80             :         std::string line;
      81       48532 :         while (std::getline(infile, line)) {
      82       48510 :                 if (line[0] == '#')
      83          66 :                         continue;
      84             : 
      85       48444 :                 std::stringstream lineStream(line);
      86             : 
      87             :                 int Z, N;
      88       48444 :                 lineStream >> Z;
      89       48444 :                 lineStream >> N;
      90             : 
      91             :                 Branch branch;
      92       48444 :                 lineStream >> branch.channel;
      93             : 
      94             :                 double r;
      95     9785688 :                 for (size_t i = 0; i < nlg; i++) {
      96             :                         lineStream >> r;
      97     9737244 :                         branch.branchingRatio.push_back(r);
      98             :                 }
      99             : 
     100       48444 :                 pdBranch[Z * 31 + N].push_back(branch);
     101       48444 :         }
     102             : 
     103          22 :         infile.close();
     104          22 : }
     105             : 
     106          22 : void PhotoDisintegration::initPhotonEmission(std::string filename) {
     107          22 :         std::ifstream infile(filename.c_str());
     108          22 :         if (not infile.good())
     109           0 :                 throw std::runtime_error("PhotoDisintegration: could not open file " + filename);
     110             : 
     111             :         // clear previously loaded emission probabilities
     112          22 :         pdPhoton.clear();
     113             : 
     114             :         std::string line;
     115      209154 :         while (std::getline(infile, line)) {
     116      209132 :                 if (line[0] == '#')
     117          66 :                         continue;
     118             : 
     119      209066 :                 std::stringstream lineStream(line);
     120             : 
     121             :                 int Z, N, Zd, Nd;
     122      209066 :                 lineStream >> Z;
     123      209066 :                 lineStream >> N;
     124      209066 :                 lineStream >> Zd;
     125      209066 :                 lineStream >> Nd;
     126             : 
     127             :                 PhotonEmission em;
     128             :                 lineStream >> em.energy;
     129      209066 :                 em.energy *= eV;
     130             : 
     131             :                 double r;
     132    42231332 :                 for (size_t i = 0; i < nlg; i++) {
     133             :                         lineStream >> r;
     134    42022266 :                         em.emissionProbability.push_back(r);
     135             :                 }
     136             : 
     137      209066 :                 int key = Z * 1000000 + N * 10000 + Zd * 100 + Nd;
     138      209066 :                 if (pdPhoton.find(key) == pdPhoton.end()) {
     139             :                         std::vector<PhotonEmission> emissions;
     140       41096 :                         pdPhoton[key] = emissions;
     141       41096 :                 }
     142      209066 :                 pdPhoton[key].push_back(em);
     143      209066 :         }
     144             : 
     145          22 :         infile.close();
     146          22 : }
     147             : 
     148       66765 : void PhotoDisintegration::process(Candidate *candidate) const {
     149             :         // execute the loop at least once for limiting the next step
     150       66765 :         double step = candidate->getCurrentStep();
     151             :         do {
     152             :                 // check if nucleus
     153       67044 :                 int id = candidate->current.getId();
     154       67044 :                 if (not isNucleus(id))
     155             :                         return;
     156             : 
     157       67043 :                 int A = massNumber(id);
     158       67043 :                 int Z = chargeNumber(id);
     159       67043 :                 int N = A - Z;
     160       67043 :                 size_t idx = Z * 31 + N;
     161             : 
     162             :                 // check if disintegration data available
     163       67043 :                 if ((Z > 26) or (N > 30))
     164             :                         return;
     165       67043 :                 if (pdRate[idx].size() == 0)
     166             :                         return;
     167             : 
     168             :                 // check if in tabulated energy range
     169        1193 :                 double z = candidate->getRedshift();
     170        1193 :                 double lg = log10(candidate->current.getLorentzFactor() * (1 + z));
     171        1193 :                 if ((lg <= lgmin) or (lg >= lgmax))
     172             :                         return;
     173             : 
     174        1193 :                 double rate = interpolateEquidistant(lg, lgmin, lgmax, pdRate[idx]);
     175        1193 :                 rate *= pow_integer<2>(1 + z) * photonField->getRedshiftScaling(z); // cosmological scaling, rate per comoving distance
     176             : 
     177             :                 // check if interaction occurs in this step
     178             :                 // otherwise limit next step to a fraction of the mean free path
     179        1193 :                 Random &random = Random::instance();
     180        1193 :                 double randDist = -log(random.rand()) / rate;
     181        1193 :                 if (step < randDist) {
     182         914 :                         candidate->limitNextStep(limit / rate);
     183         914 :                         return;
     184             :                 }
     185             : 
     186             :                 // select channel and interact
     187             :                 const std::vector<Branch> &branches = pdBranch[idx];
     188         279 :                 double cmp = random.rand();
     189         279 :                 int l = round((lg - lgmin) / (lgmax - lgmin) * (nlg - 1)); // index of closest tabulation point
     190             :                 size_t i = 0;
     191        2085 :                 while ((i < branches.size()) and (cmp > 0)) {
     192        1806 :                         cmp -= branches[i].branchingRatio[l];
     193        1806 :                         i++;
     194             :                 }
     195         279 :                 performInteraction(candidate, branches[i-1].channel);
     196             : 
     197             :                 // repeat with remaining step
     198         279 :                 step -= randDist;
     199         279 :         } while (step > 0);
     200             : }
     201             : 
     202         281 : void PhotoDisintegration::performInteraction(Candidate *candidate, int channel) const {
     203         281 :         KISS_LOG_DEBUG << "Photodisintegration::performInteraction. Channel " <<  channel << " on candidate " << candidate->getDescription(); 
     204             :         // parse disintegration channel
     205             :         int nNeutron = digit(channel, 100000);
     206             :         int nProton = digit(channel, 10000);
     207             :         int nH2 = digit(channel, 1000);
     208             :         int nH3 = digit(channel, 100);
     209             :         int nHe3 = digit(channel, 10);
     210             :         int nHe4 = digit(channel, 1);
     211             : 
     212         281 :         int dA = -nNeutron - nProton - 2 * nH2 - 3 * nH3 - 3 * nHe3 - 4 * nHe4;
     213         281 :         int dZ = -nProton - nH2 - nH3 - 2 * nHe3 - 2 * nHe4;
     214             : 
     215         281 :         int id = candidate->current.getId();
     216         281 :         int A = massNumber(id);
     217         281 :         int Z = chargeNumber(id);
     218         281 :         double EpA = candidate->current.getEnergy() / A;
     219             : 
     220             :         // create secondaries
     221         281 :         Random &random = Random::instance();
     222         281 :         Vector3d pos = random.randomInterpolatedPosition(candidate->previous.getPosition(), candidate->current.getPosition());
     223             :         try
     224             :         {
     225         518 :                 for (size_t i = 0; i < nNeutron; i++)
     226         237 :                         candidate->addSecondary(nucleusId(1, 0), EpA, pos, 1., interactionTag);
     227         389 :                 for (size_t i = 0; i < nProton; i++)
     228         108 :                         candidate->addSecondary(nucleusId(1, 1), EpA, pos, 1., interactionTag);
     229         283 :                 for (size_t i = 0; i < nH2; i++)
     230           2 :                         candidate->addSecondary(nucleusId(2, 1), EpA * 2, pos, 1., interactionTag);
     231         281 :                 for (size_t i = 0; i < nH3; i++)
     232           0 :                         candidate->addSecondary(nucleusId(3, 1), EpA * 3, pos, 1., interactionTag);
     233         281 :                 for (size_t i = 0; i < nHe3; i++)
     234           0 :                         candidate->addSecondary(nucleusId(3, 2), EpA * 3, pos, 1., interactionTag);
     235         331 :                 for (size_t i = 0; i < nHe4; i++)
     236          50 :                         candidate->addSecondary(nucleusId(4, 2), EpA * 4, pos, 1., interactionTag);
     237             : 
     238             : 
     239             :         // update particle
     240             :           candidate->created = candidate->current;
     241         281 :                 candidate->current.setId(nucleusId(A + dA, Z + dZ));
     242         281 :                 candidate->current.setEnergy(EpA * (A + dA));
     243             :         }
     244           0 :         catch (std::runtime_error &e)
     245             :         {
     246           0 :                 KISS_LOG_ERROR << "Something went wrong in the PhotoDisentigration\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();
     247           0 :                 throw;
     248           0 :         }
     249             : 
     250         281 :         if (not havePhotons)
     251             :                 return;
     252             : 
     253             :         // create photons
     254          30 :         double z = candidate->getRedshift();
     255          30 :         double lg = log10(candidate->current.getLorentzFactor() * (1 + z));
     256          30 :         double lf = candidate->current.getLorentzFactor();
     257             : 
     258          30 :         int l = round((lg - lgmin) / (lgmax - lgmin) * (nlg - 1));  // index of closest tabulation point
     259          30 :         int key = Z*1e6 + (A-Z)*1e4 + (Z+dZ)*1e2 + (A+dA) - (Z+dZ);
     260             : 
     261         212 :         for (int i = 0; i < pdPhoton[key].size(); i++) {
     262             :                 // check for random emission
     263         182 :                 if (random.rand() > pdPhoton[key][i].emissionProbability[l])
     264         158 :                         continue;
     265             : 
     266             :                 // boost to lab frame
     267          24 :                 double cosTheta = 2 * random.rand() - 1;
     268          24 :                 double E = pdPhoton[key][i].energy * lf * (1 - cosTheta);
     269          24 :                 candidate->addSecondary(22, E, pos, 1., interactionTag);
     270             :         }
     271             : }
     272             : 
     273           0 : double PhotoDisintegration::lossLength(int id, double gamma, double z) {
     274             :         // check if nucleus
     275           0 :         if (not (isNucleus(id)))
     276             :                 return std::numeric_limits<double>::max();
     277             : 
     278           0 :         int A = massNumber(id);
     279           0 :         int Z = chargeNumber(id);
     280           0 :         int N = A - Z;
     281           0 :         size_t idx = Z * 31 + N;
     282             : 
     283             :         // check if disintegration data available
     284           0 :         if ((Z > 26) or (N > 30))
     285             :                 return std::numeric_limits<double>::max();
     286             :         const std::vector<double> &rate = pdRate[idx];
     287           0 :         if (rate.size() == 0)
     288             :                 return std::numeric_limits<double>::max();
     289             : 
     290             :         // check if in tabulated energy range
     291           0 :         double lg = log10(gamma * (1 + z));
     292           0 :         if ((lg <= lgmin) or (lg >= lgmax))
     293             :                 return std::numeric_limits<double>::max();
     294             : 
     295             :         // total interaction rate
     296           0 :         double lossRate = interpolateEquidistant(lg, lgmin, lgmax, rate);
     297             : 
     298             :         // comological scaling, rate per physical distance
     299           0 :         lossRate *= pow_integer<3>(1 + z) * photonField->getRedshiftScaling(z);
     300             : 
     301             :         // average number of nucleons lost for all disintegration channels
     302             :         double avg_dA = 0;
     303             :         const std::vector<Branch> &branches = pdBranch[idx];
     304           0 :         for (size_t i = 0; i < branches.size(); i++) {
     305           0 :                 int channel = branches[i].channel;
     306             :                 int dA = 0;
     307             :                 dA += 1 * digit(channel, 100000);
     308           0 :                 dA += 1 * digit(channel, 10000);
     309           0 :                 dA += 2 * digit(channel, 1000);
     310           0 :                 dA += 3 * digit(channel, 100);
     311           0 :                 dA += 3 * digit(channel, 10);
     312           0 :                 dA += 4 * digit(channel, 1);
     313             : 
     314           0 :                 double br = interpolateEquidistant(lg, lgmin, lgmax, branches[i].branchingRatio);
     315           0 :                 avg_dA += br * dA;
     316             :         }
     317             : 
     318           0 :         lossRate *= avg_dA / A;
     319           0 :         return 1 / lossRate;
     320             : }
     321             : 
     322           1 : void PhotoDisintegration::setInteractionTag(std::string tag) {
     323           1 :         interactionTag = tag;
     324           1 : }
     325             : 
     326           2 : std::string PhotoDisintegration::getInteractionTag() const {
     327           2 :         return interactionTag;
     328             : }
     329             : 
     330             : } // namespace crpropa

Generated by: LCOV version 1.14