Program Listing for File PhotoDisintegration.cpp
↰ Return to documentation for file (src/module/PhotoDisintegration.cpp
)
#include "crpropa/module/PhotoDisintegration.h"
#include "crpropa/Units.h"
#include "crpropa/ParticleID.h"
#include "crpropa/ParticleMass.h"
#include "crpropa/Random.h"
#include "kiss/logger.h"
#include <cmath>
#include <limits>
#include <sstream>
#include <fstream>
#include <stdexcept>
namespace crpropa {
const double PhotoDisintegration::lgmin = 6; // minimum log10(Lorentz-factor)
const double PhotoDisintegration::lgmax = 14; // maximum log10(Lorentz-factor)
const size_t PhotoDisintegration::nlg = 201; // number of Lorentz-factor steps
PhotoDisintegration::PhotoDisintegration(ref_ptr<PhotonField> f, bool havePhotons, double limit) {
setPhotonField(f);
this->havePhotons = havePhotons;
this->limit = limit;
}
void PhotoDisintegration::setPhotonField(ref_ptr<PhotonField> photonField) {
this->photonField = photonField;
std::string fname = photonField->getFieldName();
setDescription("PhotoDisintegration: " + fname);
initRate(getDataPath("Photodisintegration/rate_" + fname + ".txt"));
initBranching(getDataPath("Photodisintegration/branching_" + fname + ".txt"));
initPhotonEmission(getDataPath("Photodisintegration/photon_emission_" + fname.substr(0,3) + ".txt"));
}
void PhotoDisintegration::setHavePhotons(bool havePhotons) {
this->havePhotons = havePhotons;
}
void PhotoDisintegration::setLimit(double limit) {
this->limit = limit;
}
void PhotoDisintegration::initRate(std::string filename) {
std::ifstream infile(filename.c_str());
if (not infile.good())
throw std::runtime_error("PhotoDisintegration: could not open file " + filename);
// clear previously loaded interaction rates
pdRate.clear();
pdRate.resize(27 * 31);
std::string line;
while (std::getline(infile, line)) {
if (line[0] == '#')
continue;
std::stringstream lineStream(line);
int Z, N;
lineStream >> Z;
lineStream >> N;
double r;
for (size_t i = 0; i < nlg; i++) {
lineStream >> r;
pdRate[Z * 31 + N].push_back(r / Mpc);
}
}
infile.close();
}
void PhotoDisintegration::initBranching(std::string filename) {
std::ifstream infile(filename.c_str());
if (not infile.good())
throw std::runtime_error("PhotoDisintegration: could not open file " + filename);
// clear previously loaded interaction rates
pdBranch.clear();
pdBranch.resize(27 * 31);
std::string line;
while (std::getline(infile, line)) {
if (line[0] == '#')
continue;
std::stringstream lineStream(line);
int Z, N;
lineStream >> Z;
lineStream >> N;
Branch branch;
lineStream >> branch.channel;
double r;
for (size_t i = 0; i < nlg; i++) {
lineStream >> r;
branch.branchingRatio.push_back(r);
}
pdBranch[Z * 31 + N].push_back(branch);
}
infile.close();
}
void PhotoDisintegration::initPhotonEmission(std::string filename) {
std::ifstream infile(filename.c_str());
if (not infile.good())
throw std::runtime_error("PhotoDisintegration: could not open file " + filename);
// clear previously loaded emission probabilities
pdPhoton.clear();
std::string line;
while (std::getline(infile, line)) {
if (line[0] == '#')
continue;
std::stringstream lineStream(line);
int Z, N, Zd, Nd;
lineStream >> Z;
lineStream >> N;
lineStream >> Zd;
lineStream >> Nd;
PhotonEmission em;
lineStream >> em.energy;
em.energy *= eV;
double r;
for (size_t i = 0; i < nlg; i++) {
lineStream >> r;
em.emissionProbability.push_back(r);
}
int key = Z * 1000000 + N * 10000 + Zd * 100 + Nd;
if (pdPhoton.find(key) == pdPhoton.end()) {
std::vector<PhotonEmission> emissions;
pdPhoton[key] = emissions;
}
pdPhoton[key].push_back(em);
}
infile.close();
}
void PhotoDisintegration::process(Candidate *candidate) const {
// execute the loop at least once for limiting the next step
double step = candidate->getCurrentStep();
do {
// check if nucleus
int id = candidate->current.getId();
if (not isNucleus(id))
return;
int A = massNumber(id);
int Z = chargeNumber(id);
int N = A - Z;
size_t idx = Z * 31 + N;
// check if disintegration data available
if ((Z > 26) or (N > 30))
return;
if (pdRate[idx].size() == 0)
return;
// check if in tabulated energy range
double z = candidate->getRedshift();
double lg = log10(candidate->current.getLorentzFactor() * (1 + z));
if ((lg <= lgmin) or (lg >= lgmax))
return;
double rate = interpolateEquidistant(lg, lgmin, lgmax, pdRate[idx]);
rate *= pow_integer<2>(1 + z) * photonField->getRedshiftScaling(z); // cosmological scaling, rate per comoving distance
// check if interaction occurs in this step
// otherwise limit next step to a fraction of the mean free path
Random &random = Random::instance();
double randDist = -log(random.rand()) / rate;
if (step < randDist) {
candidate->limitNextStep(limit / rate);
return;
}
// select channel and interact
const std::vector<Branch> &branches = pdBranch[idx];
double cmp = random.rand();
int l = round((lg - lgmin) / (lgmax - lgmin) * (nlg - 1)); // index of closest tabulation point
size_t i = 0;
while ((i < branches.size()) and (cmp > 0)) {
cmp -= branches[i].branchingRatio[l];
i++;
}
performInteraction(candidate, branches[i-1].channel);
// repeat with remaining step
step -= randDist;
} while (step > 0);
}
void PhotoDisintegration::performInteraction(Candidate *candidate, int channel) const {
KISS_LOG_DEBUG << "Photodisintegration::performInteraction. Channel " << channel << " on candidate " << candidate->getDescription();
// parse disintegration channel
int nNeutron = digit(channel, 100000);
int nProton = digit(channel, 10000);
int nH2 = digit(channel, 1000);
int nH3 = digit(channel, 100);
int nHe3 = digit(channel, 10);
int nHe4 = digit(channel, 1);
int dA = -nNeutron - nProton - 2 * nH2 - 3 * nH3 - 3 * nHe3 - 4 * nHe4;
int dZ = -nProton - nH2 - nH3 - 2 * nHe3 - 2 * nHe4;
int id = candidate->current.getId();
int A = massNumber(id);
int Z = chargeNumber(id);
double EpA = candidate->current.getEnergy() / A;
// create secondaries
Random &random = Random::instance();
Vector3d pos = random.randomInterpolatedPosition(candidate->previous.getPosition(), candidate->current.getPosition());
try
{
for (size_t i = 0; i < nNeutron; i++)
candidate->addSecondary(nucleusId(1, 0), EpA, pos, 1., interactionTag);
for (size_t i = 0; i < nProton; i++)
candidate->addSecondary(nucleusId(1, 1), EpA, pos, 1., interactionTag);
for (size_t i = 0; i < nH2; i++)
candidate->addSecondary(nucleusId(2, 1), EpA * 2, pos, 1., interactionTag);
for (size_t i = 0; i < nH3; i++)
candidate->addSecondary(nucleusId(3, 1), EpA * 3, pos, 1., interactionTag);
for (size_t i = 0; i < nHe3; i++)
candidate->addSecondary(nucleusId(3, 2), EpA * 3, pos, 1., interactionTag);
for (size_t i = 0; i < nHe4; i++)
candidate->addSecondary(nucleusId(4, 2), EpA * 4, pos, 1., interactionTag);
// update particle
candidate->created = candidate->current;
candidate->current.setId(nucleusId(A + dA, Z + dZ));
candidate->current.setEnergy(EpA * (A + dA));
}
catch (std::runtime_error &e)
{
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();
throw;
}
if (not havePhotons)
return;
// create photons
double z = candidate->getRedshift();
double lg = log10(candidate->current.getLorentzFactor() * (1 + z));
double lf = candidate->current.getLorentzFactor();
int l = round((lg - lgmin) / (lgmax - lgmin) * (nlg - 1)); // index of closest tabulation point
int key = Z*1e6 + (A-Z)*1e4 + (Z+dZ)*1e2 + (A+dA) - (Z+dZ);
for (int i = 0; i < pdPhoton[key].size(); i++) {
// check for random emission
if (random.rand() > pdPhoton[key][i].emissionProbability[l])
continue;
// boost to lab frame
double cosTheta = 2 * random.rand() - 1;
double E = pdPhoton[key][i].energy * lf * (1 - cosTheta);
candidate->addSecondary(22, E, pos, 1., interactionTag);
}
}
double PhotoDisintegration::lossLength(int id, double gamma, double z) {
// check if nucleus
if (not (isNucleus(id)))
return std::numeric_limits<double>::max();
int A = massNumber(id);
int Z = chargeNumber(id);
int N = A - Z;
size_t idx = Z * 31 + N;
// check if disintegration data available
if ((Z > 26) or (N > 30))
return std::numeric_limits<double>::max();
const std::vector<double> &rate = pdRate[idx];
if (rate.size() == 0)
return std::numeric_limits<double>::max();
// check if in tabulated energy range
double lg = log10(gamma * (1 + z));
if ((lg <= lgmin) or (lg >= lgmax))
return std::numeric_limits<double>::max();
// total interaction rate
double lossRate = interpolateEquidistant(lg, lgmin, lgmax, rate);
// comological scaling, rate per physical distance
lossRate *= pow_integer<3>(1 + z) * photonField->getRedshiftScaling(z);
// average number of nucleons lost for all disintegration channels
double avg_dA = 0;
const std::vector<Branch> &branches = pdBranch[idx];
for (size_t i = 0; i < branches.size(); i++) {
int channel = branches[i].channel;
int dA = 0;
dA += 1 * digit(channel, 100000);
dA += 1 * digit(channel, 10000);
dA += 2 * digit(channel, 1000);
dA += 3 * digit(channel, 100);
dA += 3 * digit(channel, 10);
dA += 4 * digit(channel, 1);
double br = interpolateEquidistant(lg, lgmin, lgmax, branches[i].branchingRatio);
avg_dA += br * dA;
}
lossRate *= avg_dA / A;
return 1 / lossRate;
}
void PhotoDisintegration::setInteractionTag(std::string tag) {
interactionTag = tag;
}
std::string PhotoDisintegration::getInteractionTag() const {
return interactionTag;
}
} // namespace crpropa