Program Listing for File EMInverseComptonScattering.cpp
↰ Return to documentation for file (src/module/EMInverseComptonScattering.cpp
)
#include "crpropa/module/EMInverseComptonScattering.h"
#include "crpropa/Units.h"
#include "crpropa/Random.h"
#include "crpropa/Common.h"
#include <fstream>
#include <limits>
#include <stdexcept>
namespace crpropa {
static const double mec2 = mass_electron * c_squared;
EMInverseComptonScattering::EMInverseComptonScattering(ref_ptr<PhotonField> photonField, bool havePhotons, double thinning, double limit) {
setPhotonField(photonField);
setHavePhotons(havePhotons);
setLimit(limit);
setThinning(thinning);
}
void EMInverseComptonScattering::setPhotonField(ref_ptr<PhotonField> photonField) {
this->photonField = photonField;
std::string fname = photonField->getFieldName();
setDescription("EMInverseComptonScattering: " + fname);
initRate(getDataPath("EMInverseComptonScattering/rate_" + fname + ".txt"));
initCumulativeRate(getDataPath("EMInverseComptonScattering/cdf_" + fname + ".txt"));
}
void EMInverseComptonScattering::setHavePhotons(bool havePhotons) {
this->havePhotons = havePhotons;
}
void EMInverseComptonScattering::setLimit(double limit) {
this->limit = limit;
}
void EMInverseComptonScattering::setThinning(double thinning) {
this->thinning = thinning;
}
void EMInverseComptonScattering::initRate(std::string filename) {
std::ifstream infile(filename.c_str());
if (!infile.good())
throw std::runtime_error("EMInverseComptonScattering: could not open file " + filename);
// clear previously loaded tables
tabEnergy.clear();
tabRate.clear();
while (infile.good()) {
if (infile.peek() != '#') {
double a, b;
infile >> a >> b;
if (infile) {
tabEnergy.push_back(pow(10, a) * eV);
tabRate.push_back(b / Mpc);
}
}
infile.ignore(std::numeric_limits < std::streamsize > ::max(), '\n');
}
infile.close();
}
void EMInverseComptonScattering::initCumulativeRate(std::string filename) {
std::ifstream infile(filename.c_str());
if (!infile.good())
throw std::runtime_error("EMInverseComptonScattering: could not open file " + filename);
// clear previously loaded tables
tabE.clear();
tabs.clear();
tabCDF.clear();
// skip header
while (infile.peek() == '#')
infile.ignore(std::numeric_limits < std::streamsize > ::max(), '\n');
// read s values in first line
double a;
infile >> a; // skip first value
while (infile.good() and (infile.peek() != '\n')) {
infile >> a;
tabs.push_back(pow(10, a) * eV * eV);
}
// read all following lines: E, cdf values
while (infile.good()) {
infile >> a;
if (!infile)
break; // end of file
tabE.push_back(pow(10, a) * eV);
std::vector<double> cdf;
for (int i = 0; i < tabs.size(); i++) {
infile >> a;
cdf.push_back(a / Mpc);
}
tabCDF.push_back(cdf);
}
infile.close();
}
// Class to calculate the energy distribution of the ICS photon and to sample from it
class ICSSecondariesEnergyDistribution {
private:
std::vector< std::vector<double> > data;
std::vector<double> s_values;
size_t Ns;
size_t Nrer;
double s_min;
double s_max;
double dls;
public:
// differential cross-section, see Lee '96 (arXiv:9604098), eq. 23 for x = Ee'/Ee
double dSigmadE(double x, double beta) {
double q = ((1 - beta) / beta) * (1 - 1./x);
return ((1 + beta) / beta) * (x + 1./x + 2 * q + q * q);
}
// create the cumulative energy distribution of the up-scattered photon
ICSSecondariesEnergyDistribution() {
Ns = 1000;
Nrer = 1000;
s_min = mec2 * mec2;
s_max = 2e23 * eV * eV;
dls = (log(s_max) - log(s_min)) / Ns;
data = std::vector< std::vector<double> >(1000, std::vector<double>(1000));
std::vector<double> data_i(1000);
// tabulate s bin borders
s_values = std::vector<double>(1001);
for (size_t i = 0; i < Ns + 1; ++i)
s_values[i] = s_min * exp(i*dls);
// for each s tabulate cumulative differential cross section
for (size_t i = 0; i < Ns; i++) {
double s = s_min * exp((i+0.5) * dls);
double beta = (s - s_min) / (s + s_min);
double x0 = (1 - beta) / (1 + beta);
double dlx = -log(x0) / Nrer;
// cumulative midpoint integration
data_i[0] = dSigmadE(x0, beta) * expm1(dlx);
for (size_t j = 1; j < Nrer; j++) {
double x = x0 * exp((j+0.5) * dlx);
double dx = exp((j+1) * dlx) - exp(j * dlx);
data_i[j] = dSigmadE(x, beta) * dx;
data_i[j] += data_i[j-1];
}
data[i] = data_i;
}
}
// draw random energy for the up-scattered photon Ep(Ee, s)
double sample(double Ee, double s) {
size_t idx = std::lower_bound(s_values.begin(), s_values.end(), s) - s_values.begin();
std::vector<double> s0 = data[idx];
Random &random = Random::instance();
size_t j = random.randBin(s0) + 1; // draw random bin (upper bin boundary returned)
double beta = (s - s_min) / (s + s_min);
double x0 = (1 - beta) / (1 + beta);
double dlx = -log(x0) / Nrer;
double binWidth = x0 * (exp(j * dlx) - exp((j-1) * dlx));
double Ep = (x0 * exp((j-1) * dlx) + binWidth) * Ee;
return std::min(Ee, Ep); // prevent Ep > Ee from numerical inaccuracies
}
};
void EMInverseComptonScattering::performInteraction(Candidate *candidate) const {
// scale the particle energy instead of background photons
double z = candidate->getRedshift();
double E = candidate->current.getEnergy() * (1 + z);
if (E < tabE.front() or E > tabE.back())
return;
// sample the value of s
Random &random = Random::instance();
size_t i = closestIndex(E, tabE);
size_t j = random.randBin(tabCDF[i]);
double s_kin = pow(10, log10(tabs[j]) + (random.rand() - 0.5) * 0.1);
double s = s_kin + mec2 * mec2;
// sample electron energy after scattering
static ICSSecondariesEnergyDistribution distribution;
double Enew = distribution.sample(E, s);
// add up-scattered photon
double Esecondary = E - Enew;
double f = Enew / E;
if (havePhotons) {
if (random.rand() < pow(1 - f, thinning)) {
double w = 1. / pow(1 - f, thinning);
Vector3d pos = random.randomInterpolatedPosition(candidate->previous.getPosition(), candidate->current.getPosition());
candidate->addSecondary(22, Esecondary / (1 + z), pos, w, interactionTag);
}
}
// update the primary particle energy; do this after adding the secondary to correctly set the secondary's parent
candidate->current.setEnergy(Enew / (1 + z));
}
void EMInverseComptonScattering::process(Candidate *candidate) const {
// check if electron / positron
int id = candidate->current.getId();
if (abs(id) != 11)
return;
// scale the particle energy instead of background photons
double z = candidate->getRedshift();
double E = candidate->current.getEnergy() * (1 + z);
if (E < tabEnergy.front() or (E > tabEnergy.back()))
return;
// interaction rate
double rate = interpolate(E, tabEnergy, tabRate);
rate *= pow_integer<2>(1 + z) * photonField->getRedshiftScaling(z);
// 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 ocurr, limit next step
if (step < randDistance) {
candidate->limitNextStep(limit / rate);
return;
}
performInteraction(candidate);
// repeat with remaining step
step -= randDistance;
} while (step > 0);
}
void EMInverseComptonScattering::setInteractionTag(std::string tag) {
interactionTag = tag;
}
std::string EMInverseComptonScattering::getInteractionTag() const {
return interactionTag;
}
} // namespace crpropa