Program Listing for File PhotonPropagation.cpp

Return to documentation for file (src/PhotonPropagation.cpp)

#include "crpropa/PhotonPropagation.h"
#include "crpropa/Common.h"
#include "crpropa/Units.h"
#include "crpropa/Cosmology.h"
#include "crpropa/ProgressBar.h"

#include "EleCa/Propagation.h"
#include "EleCa/Particle.h"
#include "EleCa/Common.h"

#include "dint/prop_second.h"
#include "dint/DintEMCascade.h"
#include "kiss/string.h"
#include "kiss/logger.h"

#include <fstream>
#include <cstdio>
#include <stdexcept>
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <limits>
#include <cmath>

namespace crpropa {

void ElecaPropagation(
                const std::string &inputfile,
                const std::string &outputfile,
                bool showProgress,
                double lowerEnergyThreshold,
                double magneticFieldStrength,
                const std::string &background) {

        KISS_LOG_WARNING << "EleCa propagation is deprecated and is no longer supported. Please use the EM* (EMPairProduction, EMInverseComptonScattering, ...) modules instead.\n";

        std::ifstream infile(inputfile.c_str());
        std::streampos startPosition = infile.tellg();

        infile.seekg(0, std::ios::end);
        std::streampos endPosition = infile.tellg();
        infile.seekg(startPosition);


        ProgressBar progressbar(endPosition);
        if (showProgress) {
                progressbar.start("Run ElecaPropagation");
        }

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

        bool PhotonOutput1D;
        std::string line;
        std::getline(infile, line);
        if (line == "#ID        E       D       pID     pE      iID     iE      iD") {
                PhotonOutput1D = true;
        } else if (line == "#   D       ID      E       ID0     E0      ID1     E1      X1") {
                PhotonOutput1D = false;
        } else {
                throw std::runtime_error("ElecaPropagation: Wrong header of input file. Use PhotonOutput1D or Event1D with additional columns enabled.");
        }

        eleca::setSeed();
        eleca::Propagation propagation;
        propagation.SetEthr(lowerEnergyThreshold / eV );
        propagation.ReadTables(getDataPath("EleCa/eleca.dat"));
        propagation.InitBkgArray(background);

        propagation.SetB(magneticFieldStrength / gauss);

        std::ofstream output(outputfile.c_str());
        output << "# ID\tE\tiID\tiE\tgeneration\n";
        output << "# ID          Id of particle (photon, electron, positron)\n";
        output << "# E           Energy [EeV]\n";
        output << "# iID         Id of source particle\n";
        output << "# iE          Energy [EeV] of source particle\n";
        output << "# Generation  number of interactions during propagation before particle is created\n";

        while (infile.good()) {
                if (infile.peek() != '#') {
                        double D, E, E0, E1, X1;
                        int ID, ID0, ID1;
                        if (PhotonOutput1D) {
                                infile >> ID >> E >> X1 >> ID1 >> E1 >> ID0 >> E0 >> D;
                        } else {
                                infile >> D >> ID >> E >> ID0 >> E0 >> ID1 >> E1 >> X1;
                        }
                        if (showProgress) {
                                progressbar.setPosition(infile.tellg());
                        }

                        if (infile) {

                                double z = eleca::Mpc2z(X1);
                                eleca::Particle p0(ID, E * 1e18, z);

                                std::vector<eleca::Particle> ParticleAtMatrix;
                                std::vector<eleca::Particle> ParticleAtGround;
                                ParticleAtMatrix.push_back(p0);

                                while (ParticleAtMatrix.size() > 0) {

                                        eleca::Particle p1 = ParticleAtMatrix.back();
                                        ParticleAtMatrix.pop_back();

                                        if (p1.IsGood()) {
                                                propagation.Propagate(p1, ParticleAtMatrix,
                                                                ParticleAtGround);
                                        }
                                }

                                for (int i = 0; i < ParticleAtGround.size(); ++i) {
                                        eleca::Particle &p = ParticleAtGround[i];
                                        if (p.GetType() != 22)
                                                continue;
                                        char buffer[256];
                                        size_t bufferPos = 0;
                                        bufferPos += std::sprintf(buffer + bufferPos, "%i\t", p.GetType());
                                        bufferPos += std::sprintf(buffer + bufferPos, "%.4E\t", p.GetEnergy() / 1E18 );
                                        bufferPos += std::sprintf(buffer + bufferPos, "%i\t", ID0);
                                        bufferPos += std::sprintf(buffer + bufferPos, "%.4E\t", E0 );
                                        bufferPos += std::sprintf(buffer + bufferPos, "%i", p.Generation());
                                        bufferPos += std::sprintf(buffer + bufferPos, "\n");

                                        output.write(buffer, bufferPos);
                                }
                        }
                }

                infile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        }
        infile.close();
        output.close();
}

typedef struct _Secondary {
        double D, E, E0, E1, X1;
        int ID, ID0, ID1;
} _Secondary;

bool _SecondarySortPredicate(const _Secondary& s1, const _Secondary& s2) {
        return s1.X1 < s2.X1;
}

void FillInSpectrum(Spectrum *a, const _Secondary &s) {
        double logE = log10(s.E) + 18;  // log10(E/eV)
        int iBin = floor((logE - MIN_ENERGY_EXP) / 0.1);  // bin number from 0 - NUM_MAIN_BINS-1
        if (iBin >= NUM_MAIN_BINS) {
                std::cout << "DintPropagation: Energy too high " << logE << std::endl;
                return;
        }
        if (iBin < 0) {
                std::cout << "DintPropagation: Energy too low " << logE << std::endl;
                return;
        }
        if (s.ID == 22)
                a->spectrum[PHOTON][iBin] += 1.;
        else if (s.ID == 11)
                a->spectrum[ELECTRON][iBin] += 1.;
        else if (s.ID == -11)
                a->spectrum[POSITRON][iBin] += 1.;
        else
                std::cout << "DintPropagation: Unhandled particle ID " << s.ID << std::endl;
}

void DintPropagation(
                const std::string &inputfile,
                const std::string &outputfile,
                int IRBFlag,
                int RadioFlag,
                double magneticFieldStrength,
                double aCutcascade_Magfield) {

        KISS_LOG_WARNING << "DINT propagation is deprecated and is no longer supported. Please use the EM* (EMPairProduction, EMInverseComptonScattering, ...) modules instead.\n";

        // initialize the energy grids for DINT
        dCVector energyGrid, energyWidth;
        New_dCVector(&energyGrid, NUM_MAIN_BINS);
        New_dCVector(&energyWidth, NUM_MAIN_BINS);
        SetEnergyBins(MIN_ENERGY_EXP, &energyGrid, &energyWidth);

        std::ofstream outfile(outputfile.c_str());
        if (!outfile.good())
                throw std::runtime_error(
                                "DintPropagation: could not open file " + outputfile);

        std::ifstream infile(inputfile.c_str());
        if (!infile.good())
                throw std::runtime_error(
                                "DintPropagation: could not open file " + inputfile);

        bool PhotonOutput1D;
        std::string line;
        std::getline(infile, line);
        if (line == "#ID        E       D       pID     pE      iID     iE      iD") {
                PhotonOutput1D = true;
        } else if (line == "#   D       ID      E       ID0     E0      ID1     E1      X1") {
                PhotonOutput1D = false;
        } else {
                throw std::runtime_error("DintPropagation: Wrong header of input file. Use PhotonOutput1D or Event1D with additional columns enabled.");
        }

        // initialize the spectrum
        Spectrum finalSpectrum;
        NewSpectrum(&finalSpectrum, NUM_MAIN_BINS);
        InitializeSpectrum(&finalSpectrum);

        std::string dataPath = getDataPath("dint");
        double B = magneticFieldStrength / gauss;
        double h = H0() * Mpc / 1000;
        DintEMCascade dint(IRBFlag, RadioFlag, dataPath, B, h, omegaM(), omegaL());

        const size_t nBuffer = 7.5E7;  // maximum number of simultaneously processed particles, keep memory requirement < 1GB
        const double dMargin = 0.1;  // distance bin width in [Mpc]

        while (infile.good()) {
                // read up to nBuffer secondaries from input file
                std::vector<_Secondary> secondaries;
                secondaries.reserve(nBuffer);
                while (infile.good() && (secondaries.size() < nBuffer)) {
                        if (infile.peek() != '#') {
                                _Secondary s;
                                if (PhotonOutput1D) {
                                        infile >> s.ID >> s.E >> s.X1 >> s.ID1 >> s.E1 >> s.ID0 >> s.E0 >> s.D;
                                } else {
                                        infile >> s.D >> s.ID >> s.E >> s.ID0 >> s.E0 >> s.ID1 >> s.E1 >> s.X1;
                                }
                                s.X1 = comoving2LightTravelDistance(s.X1 * Mpc) / Mpc;  // DintEMCascade expects light travel distance
                                if (infile)
                                        secondaries.push_back(s);
                        }
                        infile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
                }

                if (secondaries.empty())
                        break;  // all secondaries processed, nothing left to do

                // sort by distance
                std::sort(secondaries.begin(), secondaries.end(),
                                _SecondarySortPredicate);

                Spectrum inputSpectrum, outputSpectrum;
                NewSpectrum(&inputSpectrum, NUM_MAIN_BINS);
                NewSpectrum(&outputSpectrum, NUM_MAIN_BINS);
                InitializeSpectrum(&inputSpectrum);

                // process secondaries
                while ((secondaries.size() > 0 ) && (secondaries.back().X1 > 0)) {
                        double Dmax = secondaries.back().X1;  // upper bound of distance bin
                        double Dmin = max(Dmax - dMargin, 0.);  // lower bound of distance bin

                        // add all secondaries within the current distance bin
                        while ((secondaries.size() > 0) && (secondaries.back().X1 > Dmin)) {
                                FillInSpectrum(&inputSpectrum, secondaries.back());
                                secondaries.pop_back();
                        }

                        // propagate to next closest particle or to D=0
                        double D = 0;
                        if (secondaries.size() > 0)
                                D = secondaries.back().X1;

                        // propagate distance step and make the output the new input spectrum
                        InitializeSpectrum(&outputSpectrum);
                        dint.propagate(Dmax, D, &inputSpectrum, &outputSpectrum, aCutcascade_Magfield);
                        SetSpectrum(&inputSpectrum, &outputSpectrum);
                }

                // add remaining secondaries at D=0 to output spectrum
                while (secondaries.size() > 0) {
                        FillInSpectrum(&inputSpectrum, secondaries.back());
                        secondaries.pop_back();
                }

                AddSpectrum(&finalSpectrum, &inputSpectrum);
                DeleteSpectrum(&outputSpectrum);
                DeleteSpectrum(&inputSpectrum);
        }

        // output
        outfile << "# logE photons electrons positrons\n";
        outfile << "#   - logE: energy bin center <log10(E/eV)>\n";
        outfile << "#   - photons, electrons, positrons: total flux weights\n";
        for (int j = 0; j < finalSpectrum.numberOfMainBins; j++) {
                double logEc = MIN_ENERGY_EXP + 0.05 + j * 1. / BINS_PER_DECADE;
                outfile << std::setw(5) << logEc;
                for (int i = 0; i < 3; i++) {
                        outfile << std::setw(13) << finalSpectrum.spectrum[i][j];
                }
                outfile << "\n";
        }
        outfile.close();

        DeleteSpectrum(&finalSpectrum);
        Delete_dCVector(&energyGrid);
        Delete_dCVector(&energyWidth);
}



bool _ParticlesAtGroundSortPredicate(const eleca::Particle& p1, const eleca::Particle& p2) {
        return p1.Getz() < p2.Getz();
}

void DintElecaPropagation(
                const std::string &inputfile,
                const std::string &outputfile,
                bool showProgress,
                double crossOverEnergy,
                double magneticFieldStrength,
                double aCutcascade_Magfield) {

        KISS_LOG_WARNING << "EleCa+DINT propagation is deprecated and is no longer supported. Please use the EM* (EMPairProduction, EMInverseComptonScattering, ...) modules instead.\n";

        //Initialize EleCa
        std::ifstream infile(inputfile.c_str());
        std::streampos startPosition = infile.tellg();

        infile.seekg(0, std::ios::end);
        std::streampos endPosition = infile.tellg();
        infile.seekg(startPosition);

        ProgressBar progressbar(endPosition);
        if (showProgress) {
                progressbar.start("Run EleCa propagation");
        }

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

        bool PhotonOutput1D;
        std::string line;
        std::getline(infile, line);
        if (line == "#ID        E       D       pID     pE      iID     iE      iD") {
                PhotonOutput1D = true;
        } else if (line == "#   D       ID      E       ID0     E0      ID1     E1      X1") {
                PhotonOutput1D = false;
        } else {
                throw std::runtime_error("DintElecaPropagation: Wrong header of input file. Use PhotonOutput1D or Event1D with additional columns enabled.");
        }

        eleca::setSeed();
        eleca::Propagation propagation;
        propagation.SetEthr(crossOverEnergy / eV );
        propagation.ReadTables(getDataPath("EleCa/eleca.dat"));
        propagation.InitBkgArray("ALL");
        propagation.SetB(magneticFieldStrength / gauss);
        std::vector<eleca::Particle> ParticleAtGround;

        //Initialize DINT
        dCVector energyGrid, energyWidth;
        // Initialize the energy grids for dint
        New_dCVector(&energyGrid, NUM_MAIN_BINS);
        New_dCVector(&energyWidth, NUM_MAIN_BINS);
        SetEnergyBins(MIN_ENERGY_EXP, &energyGrid, &energyWidth);

        std::ofstream outfile(outputfile.c_str());
        if (!outfile.good())
                throw std::runtime_error(
                                "DintPropagation: could not open file " + outputfile);

        Spectrum finalSpectrum;
        NewSpectrum(&finalSpectrum, NUM_MAIN_BINS);
        InitializeSpectrum(&finalSpectrum);

        std::string dataPath = getDataPath("dint");
        double h = H0() * Mpc / 1000;
        double ol = omegaL();
        double om = omegaM();
        DintEMCascade dint(4, 4, dataPath, magneticFieldStrength/gauss, h, om, ol);

        // Loop over infile

        while (infile.good()) {
                if (infile.peek() == '#') {
                        infile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
                        continue;
                }

                double D, E, E0, E1, X1;
                int ID, ID0, ID1;
                if (PhotonOutput1D) {
                        infile >> ID >> E >> X1 >> ID1 >> E1 >> ID0 >> E0 >> D;
                } else {
                        infile >> D >> ID >> E >> ID0 >> E0 >> ID1 >> E1 >> X1;
                }
                if (showProgress) {
                        progressbar.setPosition(infile.tellg());
                }
                if (infile) { // stop at last line
                        double z = eleca::Mpc2z(X1);
                        eleca::Particle p0(ID, E * 1e18, z);

                        std::vector<eleca::Particle> ParticleAtMatrix;
                        ParticleAtMatrix.push_back(p0);

                        while (ParticleAtMatrix.size() > 0) {
                                eleca::Particle p1 = ParticleAtMatrix.back();
                                ParticleAtMatrix.pop_back();

                                if (p1.IsGood()) {
                                        propagation.Propagate(p1, ParticleAtMatrix,
                                                        ParticleAtGround, false);
                                }
                        }
                }

                // The vector is larger than ~1GB, or the infile is completely read - better call DINT.
                if (ParticleAtGround.size() > 1000000 || !infile) {
                        const double dMargin = 0.1 * Mpc;

                        std::sort(ParticleAtGround.begin(), ParticleAtGround.end(), _ParticlesAtGroundSortPredicate);

                        Spectrum inputSpectrum, outputSpectrum;
                        NewSpectrum(&inputSpectrum, NUM_MAIN_BINS);
                        NewSpectrum(&outputSpectrum, NUM_MAIN_BINS);

                        InitializeSpectrum(&inputSpectrum);
                        // process secondaries
                        while (ParticleAtGround.size() > 0) {
                                double currentDistance =  redshift2LightTravelDistance(ParticleAtGround.back().Getz());  // dint expects light travel distance
                                bool lastStep = (currentDistance == 0.);
                                // add secondaries at the current distance to spectrum
                                while ((ParticleAtGround.size() > 0) && (redshift2LightTravelDistance(ParticleAtGround.back().Getz()) >= (currentDistance - dMargin)))  {
                                        if (redshift2LightTravelDistance(ParticleAtGround.back().Getz()) > 0. || lastStep) {
                                                double criticalEnergy = ParticleAtGround.back().GetEnergy() / (ELECTRON_MASS); // units of dint
                                                int maxBin = (int) ((log10(criticalEnergy * ELECTRON_MASS) - MIN_ENERGY_EXP) * BINS_PER_DECADE + 0.5 + 1); // +1 line before to avoid conversion error to int for negative values (int(-0.7) = 0)
                                                maxBin -= 1; // remove the additional 1 from line before
                                                if (maxBin >= NUM_MAIN_BINS) {
                                                        std::cout << "DintPropagation: Energy too high " <<
                                                                ParticleAtGround.back().GetEnergy() << " eV"  <<
                                                                std::endl;
                                                        ParticleAtGround.pop_back();
                                                        continue;
                                                }
                                                if (maxBin < 0) {
                                                        std::cout << "DintPropagation: Energy too low " <<
                                                                ParticleAtGround.back().GetEnergy() << " eV"  << std::endl;
                                                        ParticleAtGround.pop_back();
                                                        continue;
                                                }
                                                int Id = ParticleAtGround.back().GetType();
                                                if (Id == 22)
                                                        inputSpectrum.spectrum[PHOTON][maxBin] += 1.;
                                                else if (Id == 11)
                                                        inputSpectrum.spectrum[ELECTRON][maxBin] += 1.;
                                                else if (Id == -11)
                                                        inputSpectrum.spectrum[POSITRON][maxBin] += 1.;
                                                else {
                                                        std::cout << "DintPropagation: Unhandled particle ID " << Id
                                                                << std::endl;
                                                }
                                                ParticleAtGround.pop_back();
                                        } else
                                                break;
                                }

                                double D = 0;
                                // only propagate to next particle
                                if (ParticleAtGround.size() > 0)
                                        D = redshift2LightTravelDistance(ParticleAtGround.back().Getz());

                                InitializeSpectrum(&outputSpectrum);
                                dint.propagate(currentDistance / Mpc, D / Mpc, &inputSpectrum,
                                                &outputSpectrum, aCutcascade_Magfield);
                                SetSpectrum(&inputSpectrum, &outputSpectrum);
                        } // while (secondaries.size() > 0)

                        AddSpectrum(&finalSpectrum, &inputSpectrum);

                        DeleteSpectrum(&outputSpectrum);
                        DeleteSpectrum(&inputSpectrum);
                } // dint call
        }

        infile.close();

        // output
        outfile << "# logE photons electrons positrons\n";
        outfile << "#   - logE: energy bin center <log10(E/eV)>\n";
        outfile << "#   - photons, electrons, positrons: total flux weights\n";
        for (int j = 0; j < finalSpectrum.numberOfMainBins; j++) {
                double logEc = MIN_ENERGY_EXP + 0.05 + j * 1. / BINS_PER_DECADE;
                outfile << std::setw(5) << logEc;
                for (int i = 0; i < 3; i++) {
                        outfile << std::setw(13) << finalSpectrum.spectrum[i][j];
                }
                outfile << "\n";
        }
        outfile.close();

        DeleteSpectrum(&finalSpectrum);
        Delete_dCVector(&energyGrid);
        Delete_dCVector(&energyWidth);
}

} // namespace crpropa