Program Listing for File EMPairProduction.cpp
↰ Return to documentation for file (src/module/EMPairProduction.cpp)
#include "crpropa/module/EMPairProduction.h"
#include "crpropa/Units.h"
#include "crpropa/Random.h"
#include "crpropa/Common.h"
#include "kiss/logger.h"
#include <vector>
#include <cmath>
namespace crpropa {
static const double mec2 = mass_electron * c_squared;
EMPairProduction::EMPairProduction(ref_ptr<PhotonField> photonField, bool haveElectrons, double thinning, double limit, ref_ptr<Surface> surface) {
setSurface(surface);
setPhotonField(photonField);
setThinning(thinning);
setLimit(limit);
setHaveElectrons(haveElectrons);
}
void EMPairProduction::setPhotonField(ref_ptr<PhotonField> photonField) {
this->photonField = photonField;
std::string fname = photonField->getFieldName();
setDescription("EMPairProduction: " + fname);
if (!this->photonField->hasPositionDependence()){
this->interactionRates = new InteractionRatesHomogeneous(
getDataPath("EMPairProduction/rate_" + fname + ".txt"),
getDataPath("EMPairProduction/cdf_" + fname + ".txt")
);
} else {
this->interactionRates = new InteractionRatesPositionDependent(
getDataPath("EMPairProduction/"+fname+"/Rate/"),
getDataPath("EMPairProduction/"+fname+"/CumulativeRate/"),
this->surface
);
}
}
void EMPairProduction::setHaveElectrons(bool haveElectrons) {
this->haveElectrons = haveElectrons;
}
void EMPairProduction::setLimit(double limit) {
this->limit = limit;
}
void EMPairProduction::setThinning(double thinning) {
this->thinning = thinning;
}
void EMPairProduction::setSurface(ref_ptr<Surface> surface) {
this->surface = surface;
}
ref_ptr<Surface> EMPairProduction::getSurface() const {
return this->surface;
}
void EMPairProduction::setInteractionRates(ref_ptr<InteractionRates> intRates) {
this->interactionRates = intRates;
}
ref_ptr<InteractionRates> EMPairProduction::getInteractionRates() const {
return this->interactionRates;
}
void EMPairProduction::initRate(std::string path) {
this->interactionRates->initRate(path);
}
void EMPairProduction::initCumulativeRate(std::string path) {
this->interactionRates->initCumulativeRate(path);
}
// Hold an data array to interpolate the energy distribution on
class PPSecondariesEnergyDistribution {
private:
std::vector<double> tab_s;
std::vector< std::vector<double> > data;
size_t N;
public:
// differential cross section for pair production for x = Epositron/Egamma, compare Lee 96 arXiv:9604098
double dSigmadE_PPx(double x, double beta) {
double A = (x / (1. - x) + (1. - x) / x );
double B = (1. / x + 1. / (1. - x) );
double y = (1 - beta * beta);
return A + y * B - y * y / 4 * B * B;
}
PPSecondariesEnergyDistribution() {
N = 1000;
size_t Ns = 1000;
double s_min = 4 * mec2 * mec2;
double s_max = 1e23 * eV * eV;
double dls = log(s_max / s_min) / Ns;
data = std::vector< std::vector<double> >(Ns, std::vector<double>(N));
tab_s = std::vector<double>(Ns + 1);
for (size_t i = 0; i < Ns + 1; ++i)
tab_s[i] = s_min * exp(i*dls); // tabulate s bin borders
for (size_t i = 0; i < Ns; i++) {
double s = s_min * exp(i*dls + 0.5*dls);
double beta = sqrt(1 - s_min/s);
double x0 = (1 - beta) / 2;
double dx = log((1 + beta) / (1 - beta)) / N;
// cumulative midpoint integration
std::vector<double> data_i(1000);
data_i[0] = dSigmadE_PPx(x0, beta) * expm1(dx);
for (size_t j = 1; j < N; j++) {
double x = x0 * exp(j*dx + 0.5*dx);
double binWidth = exp((j+1)*dx)-exp(j*dx);
data_i[j] = dSigmadE_PPx(x, beta) * binWidth + data_i[j-1];
}
data[i] = data_i;
}
}
// sample positron energy from cdf(E, s_kin)
double sample(double E0, double s) {
// get distribution for given s
size_t idx = std::lower_bound(tab_s.begin(), tab_s.end(), s) - tab_s.begin();
if (idx > data.size())
return NAN;
std::vector<double> s0 = data[idx];
// draw random bin
Random &random = Random::instance();
size_t j = random.randBin(s0) + 1;
double s_min = 4. * mec2 * mec2;
double beta = sqrtl(1. - s_min / s);
double x0 = (1. - beta) / 2.;
double dx = log((1 + beta) / (1 - beta)) / N;
double binWidth = x0 * (exp(j*dx) - exp((j-1)*dx));
if (random.rand() < 0.5)
return E0 * (x0 * exp((j-1) * dx) + binWidth);
else
return E0 * (1 - (x0 * exp((j-1) * dx) + binWidth));
}
};
double EMPairProduction::getRate(double E, const Vector3d &position, double z) const {
return this->interactionRates->getProcessRate(E, position) * pow_integer<2>(1 + z) * photonField->getRedshiftScaling(z);
}
void EMPairProduction::performInteraction(Candidate *candidate) const {
// scale particle energy instead of background photon energy
double z = candidate->getRedshift();
double E = candidate->current.getEnergy() * (1 + z);
Vector3d position = candidate->current.getPosition();
// cosmic ray photon is lost if interaction
candidate->setActive(false);
// check if secondary electron pair needs to be produced
if (not haveElectrons)
return;
std::vector<double> tabE;
std::vector<double> tabs;
std::vector<std::vector<double>> tabCDF;
this->interactionRates->loadPerformInteractionTabs(position, tabE, tabs, tabCDF);
// check if in tabulated energy range
if (E < tabE.front() or (E > tabE.back())) {
KISS_LOG_WARNING
<< "EMPairProduction: Energy "
<< E / eV << " eV is not in tabulated range";
return;
}
// sample the value of s
Random &random = Random::instance();
size_t i = closestIndex(E, tabE); // find closest tabulation point
size_t j = random.randBin(tabCDF[i]);
if (j <= 0) {
KISS_LOG_WARNING
<< "EMPaiProduction: Sampled s value is the lowest tabulated value, which is not physical."
<< " The index j will be set to 1 to avoid division by zero.";
j = 1; // ensure j is at least 1 to avoid division by
}
double lo = std::max(4 * mec2 * mec2, tabs[j-1]); // first s-tabulation point below min(s_kin) = (2 me c^2)^2; ensure physical value
double hi = tabs[j];
double s = lo + random.rand() * (hi - lo);
// sample electron / positron energy
static PPSecondariesEnergyDistribution interpolation;
double Ee = interpolation.sample(E, s);
double Ep = E - Ee;
double f = Ep / E;
// for some backgrounds Ee=nan due to precision limitations.
if (not std::isfinite(Ee) || not std::isfinite(Ep)) {
KISS_LOG_WARNING
<< "EMPairProduction: Sampled energies are not finite for primary energy "
<< E / eV << " eV and s = " << s / (eV * eV) << " eV^2 (maximum tabulated s = "
<< tabs.back() / (eV * eV) << " eV^2).";
return;
}
// sample random position along current step
Vector3d pos = random.randomInterpolatedPosition(candidate->previous.getPosition(), candidate->current.getPosition());
// apply sampling
if (random.rand() < pow(f, thinning)) {
double w = 1. / pow(f, thinning);
candidate->addSecondary(11, Ep / (1 + z), pos, w, interactionTag);
}
if (random.rand() < pow(1 - f, thinning)) {
double w = 1. / pow(1 - f, thinning);
candidate->addSecondary(-11, Ee / (1 + z), pos, w, interactionTag);
}
}
void EMPairProduction::process(Candidate *candidate) const {
// check if photon
if (candidate->current.getId() != 22)
return;
// scale particle energy instead of background photon energy
double z = candidate->getRedshift();
double E = candidate->current.getEnergy() * (1 + z);
Vector3d position = candidate->current.getPosition();
double rate = getRate(E, position, z);
if (rate < 0)
return;
// run this loop at least once to limit the step size
double step = candidate->getCurrentStep();
Random &random = Random::instance();
do {
double randDistance = -log(random.rand()) / rate;
// check for interaction; if it doesn't occur, limit next step
if (step < randDistance) {
candidate->limitNextStep(limit / rate);
} else {
performInteraction(candidate);
return;
}
step -= randDistance;
} while (step > 0.);
}
void EMPairProduction::setInteractionTag(std::string tag) {
interactionTag = tag;
}
std::string EMPairProduction::getInteractionTag() const {
return interactionTag;
}
} // namespace crpropa