Program Listing for File SynchrotronRadiation.cpp

Return to documentation for file (src/module/SynchrotronRadiation.cpp)

#include "crpropa/module/SynchrotronRadiation.h"
#include "crpropa/Units.h"
#include "crpropa/Random.h"

#include <fstream>
#include <limits>
#include <stdexcept>

namespace crpropa {

SynchrotronRadiation::SynchrotronRadiation(ref_ptr<MagneticField> field, bool havePhotons, double thinning, int nSamples, double limit) {
        setField(field);
        setBrms(0);
        initSpectrum();
        setHavePhotons(havePhotons);
        setLimit(limit);
        setSecondaryThreshold(1e6 * eV);
        setMaximumSamples(nSamples);
}

SynchrotronRadiation::SynchrotronRadiation(double Brms, bool havePhotons, double thinning, int nSamples, double limit) {
        setBrms(Brms);
        initSpectrum();
        setHavePhotons(havePhotons);
        setLimit(limit);
        setSecondaryThreshold(1e6 * eV);
        setMaximumSamples(nSamples);
}

void SynchrotronRadiation::setField(ref_ptr<MagneticField> f) {
        this->field = f;
}

ref_ptr<MagneticField> SynchrotronRadiation::getField() {
        return field;
}

void SynchrotronRadiation::setBrms(double Brms) {
        this->Brms = Brms;
}

double SynchrotronRadiation::getBrms() {
        return Brms;
}

void SynchrotronRadiation::setHavePhotons(bool havePhotons) {
        this->havePhotons = havePhotons;
}

bool SynchrotronRadiation::getHavePhotons() {
        return havePhotons;
}

void SynchrotronRadiation::setThinning(double thinning) {
        this->thinning = thinning;
}

double SynchrotronRadiation::getThinning() {
        return thinning;
}

void SynchrotronRadiation::setLimit(double limit) {
        this->limit = limit;
}

double SynchrotronRadiation::getLimit() {
        return limit;
}

void SynchrotronRadiation::setMaximumSamples(int nmax) {
        maximumSamples = nmax;
}

int SynchrotronRadiation::getMaximumSamples() {
        return maximumSamples;
}

void SynchrotronRadiation::setSecondaryThreshold(double threshold) {
        secondaryThreshold = threshold;
}

double SynchrotronRadiation::getSecondaryThreshold() const {
        return secondaryThreshold;
}

void SynchrotronRadiation::initSpectrum() {
        std::string filename = getDataPath("Synchrotron/spectrum.txt");
        std::ifstream infile(filename.c_str());

        if (!infile.good())
                throw std::runtime_error("SynchrotronRadiation: could not open file " + filename);

        // clear previously loaded interaction rates
        tabx.clear();
        tabCDF.clear();

        while (infile.good()) {
                if (infile.peek() != '#') {
                        double a, b;
                        infile >> a >> b;
                        if (infile) {
                                tabx.push_back(pow(10, a));
                                tabCDF.push_back(b);
                        }
                }
                infile.ignore(std::numeric_limits < std::streamsize > ::max(), '\n');
        }
        infile.close();
}

void SynchrotronRadiation::process(Candidate *candidate) const {
        double charge = fabs(candidate->current.getCharge());
        if (charge == 0)
                return; // only charged particles

        // calculate gyroradius, evaluated at the current position
        double z = candidate->getRedshift();
        double B;
        if (field.valid()) {
                Vector3d Bvec = field->getField(candidate->current.getPosition(), z);
                B = Bvec.cross(candidate->current.getDirection()).getR();
        } else {
                B = sqrt(2. / 3) * Brms; // average perpendicular field component
        }
        B *= pow(1 + z, 2); // cosmological scaling
        double Rg = candidate->current.getMomentum().getR() / charge / B;

        // calculate energy loss
        double lf = candidate->current.getLorentzFactor();
        double dEdx = 1. / 6 / M_PI / epsilon0 * pow(lf * lf - 1, 2) * pow(charge / Rg, 2); // Jackson p. 770 (14.31)
        double step = candidate->getCurrentStep() / (1 + z); // step size in local frame
        double dE = step * dEdx;

        // apply energy loss and limit next step
        double E = candidate->current.getEnergy();
        candidate->current.setEnergy(E - dE);
        candidate->limitNextStep(limit * E / dEdx);

        // optionally add secondary photons
        if (not(havePhotons))
                return;

        // check if photons with energies > 14 * Ecrit are possible
        double Ecrit = 3. / 4 * h_planck / M_PI * c_light * pow(lf, 3) / Rg;
        if (14 * Ecrit < secondaryThreshold)
                return;

        // draw photons up to the total energy loss
        // if maximumSamples is reached before that, compensate the total energy afterwards
        Random &random = Random::instance();
        double dE0 = dE;
        std::vector<double> energies;
        int counter = 0;
        while (dE > 0) {
                // draw random value between 0 and maximum of corresponding cdf
                // choose bin of s where cdf(x) = cdf_rand -> x_rand
                size_t i = random.randBin(tabCDF); // draw random bin (upper bin boundary returned)
                double binWidth = (tabx[i] - tabx[i-1]);
                double x = tabx[i-1] + random.rand() * binWidth; // draw random x uniformly distributed in bin
                double Ephoton = x * Ecrit;

                // if the remaining energy is not sufficient check for random accepting
                if (Ephoton > dE) {
                        if (random.rand() > (dE / Ephoton))
                                break; // not accepted
                }

                // only activate the "per-step" sampling if maximumSamples is explicitly set.
                if (maximumSamples > 0) {
                        if (counter >= maximumSamples)
                                break;
                }

                // store energies in array
                energies.push_back(Ephoton);

                // energy loss
                dE -= Ephoton;

                // counter for sampling break condition;
                counter++;
        }

        // while loop before gave total energy which is just a fraction of the required
        double w1 = 1;
        if (maximumSamples > 0 && dE > 0)
                w1 = 1. / (1. - dE / dE0);

        // loop over sampled photons and attribute weights accordingly
        for (int i = 0; i < energies.size(); i++) {
                double Ephoton = energies[i];
                double f = Ephoton / (E - dE0);
                double w = w1 / pow(f, thinning);

                // thinning procedure: accepts only a few random secondaries
                if (random.rand() < pow(f, thinning)) {
                        Vector3d pos = random.randomInterpolatedPosition(candidate->previous.getPosition(), candidate->current.getPosition());
                        if (Ephoton > secondaryThreshold) // create only photons with energies above threshold
                                candidate->addSecondary(22, Ephoton, pos, w, interactionTag);
                }
        }
}

std::string SynchrotronRadiation::getDescription() const {
        std::stringstream s;
        s << "Synchrotron radiation";
        if (field.valid())
                s << " for specified magnetic field";
        else
                s << " for Brms = " << Brms / nG << " nG";
        if (havePhotons)
                s << ", synchrotron photons E > " << secondaryThreshold / eV << " eV";
        else
                s << ", no synchrotron photons";
        if (maximumSamples > 0)
                s << "maximum number of photon samples: " << maximumSamples;
        if (thinning > 0)
                s << "thinning parameter: " << thinning;
        return s.str();
}

void SynchrotronRadiation::setInteractionTag(std::string tag) {
        interactionTag = tag;
}

std::string SynchrotronRadiation::getInteractionTag() const {
        return interactionTag;
}

} // namespace crpropa