From 80c2867e70733fb0ea6d30bcc592225c35e01d80 Mon Sep 17 00:00:00 2001 From: Oleg Kalashev <oleg.kalashev@gmail.com> Date: Sat, 5 Dec 2020 02:06:09 +0300 Subject: [PATCH] first release version --- README.md | 15 +- src/app/crbeam/CMakeLists.txt | 37 + src/app/crbeam/CRbeam.cpp | 613 ++++++++++++++ src/app/crbeam/CRbeam.h | 106 +++ src/app/crbeam/CmdLine.cpp | 332 ++++++++ src/app/crbeam/CmdLine.h | 117 +++ src/app/crbeam/README.md | 172 ++++ src/lib/Background.cpp | 1165 +++++++++++++++++++++++++ src/lib/Background.h | 442 ++++++++++ src/lib/CMakeLists.txt | 134 +++ src/lib/Cosmology.cpp | 204 +++++ src/lib/Cosmology.h | 240 ++++++ src/lib/Debug.cpp | 265 ++++++ src/lib/Debug.h | 140 +++ src/lib/Deflection1D.cpp | 127 +++ src/lib/Deflection1D.h | 97 +++ src/lib/Deflection3D.cpp | 325 +++++++ src/lib/Deflection3D.h | 119 +++ src/lib/ElmagTest.cpp | 350 ++++++++ src/lib/ElmagTest.h | 162 ++++ src/lib/ExampleUserMain.cpp | 113 +++ src/lib/Filter.cpp | 104 +++ src/lib/Filter.h | 98 +++ src/lib/GZK.cpp | 189 +++++ src/lib/GZK.h | 61 ++ src/lib/GammaPP.cpp | 496 +++++++++++ src/lib/GammaPP.h | 94 ++ src/lib/ICS.cpp | 665 +++++++++++++++ src/lib/ICS.h | 121 +++ src/lib/Inoue12IROSpectrum.cpp | 60 ++ src/lib/Inoue12IROSpectrum.h | 60 ++ src/lib/Interaction.cpp | 66 ++ src/lib/Interaction.h | 125 +++ src/lib/Logger.cpp | 72 ++ src/lib/Logger.h | 55 ++ src/lib/MathUtils.cpp | 1015 ++++++++++++++++++++++ src/lib/MathUtils.h | 263 ++++++ src/lib/NeutronDecay.cpp | 101 +++ src/lib/NeutronDecay.h | 56 ++ src/lib/Nucleus.cpp | 420 +++++++++ src/lib/Nucleus.h | 215 +++++ src/lib/Output.cpp | 279 ++++++ src/lib/Output.h | 108 +++ src/lib/PPP.cpp | 485 +++++++++++ src/lib/PPP.h | 149 ++++ src/lib/Particle.cpp | 195 +++++ src/lib/Particle.h | 223 +++++ src/lib/ParticleStack.cpp | 210 +++++ src/lib/ParticleStack.h | 122 +++ src/lib/PhotoDisintegration.cpp | 384 +++++++++ src/lib/PhotoDisintegration.h | 86 ++ src/lib/PrecisionTests.cpp | 72 ++ src/lib/PrecisionTests.h | 49 ++ src/lib/PropagationEngine.cpp | 521 ++++++++++++ src/lib/PropagationEngine.h | 92 ++ src/lib/ProtonPP.cpp | 341 ++++++++ src/lib/ProtonPP.h | 102 +++ src/lib/Randomizer.cpp | 97 +++ src/lib/Randomizer.h | 58 ++ src/lib/Sophia.cpp | 180 ++++ src/lib/Sophia.h | 82 ++ src/lib/Stecker16Background.cpp | 60 ++ src/lib/Stecker16Background.h | 82 ++ src/lib/SteckerEBL.cpp | 166 ++++ src/lib/SteckerEBL.h | 75 ++ src/lib/TableBackgrounds.cpp | 184 ++++ src/lib/TableBackgrounds.h | 115 +++ src/lib/TableFunction.cpp | 256 ++++++ src/lib/TableFunction.h | 261 ++++++ src/lib/TableReader.cpp | 270 ++++++ src/lib/TableReader.h | 105 +++ src/lib/Test.cpp | 1414 +++++++++++++++++++++++++++++++ src/lib/Test.h | 232 +++++ src/lib/Thinning.cpp | 65 ++ src/lib/Thinning.h | 63 ++ src/lib/Units.cpp | 92 ++ src/lib/Units.h | 83 ++ src/lib/Utils.cpp | 32 + src/lib/Utils.h | 234 +++++ src/lib/main.cpp | 63 ++ src/lib/sophia2cpp.f | 85 ++ src/lib/z2t.cpp | 55 ++ 82 files changed, 17437 insertions(+), 1 deletion(-) create mode 100644 src/app/crbeam/CMakeLists.txt create mode 100644 src/app/crbeam/CRbeam.cpp create mode 100644 src/app/crbeam/CRbeam.h create mode 100644 src/app/crbeam/CmdLine.cpp create mode 100644 src/app/crbeam/CmdLine.h create mode 100644 src/app/crbeam/README.md create mode 100644 src/lib/Background.cpp create mode 100644 src/lib/Background.h create mode 100644 src/lib/CMakeLists.txt create mode 100644 src/lib/Cosmology.cpp create mode 100644 src/lib/Cosmology.h create mode 100644 src/lib/Debug.cpp create mode 100644 src/lib/Debug.h create mode 100644 src/lib/Deflection1D.cpp create mode 100644 src/lib/Deflection1D.h create mode 100644 src/lib/Deflection3D.cpp create mode 100644 src/lib/Deflection3D.h create mode 100644 src/lib/ElmagTest.cpp create mode 100644 src/lib/ElmagTest.h create mode 100644 src/lib/ExampleUserMain.cpp create mode 100644 src/lib/Filter.cpp create mode 100644 src/lib/Filter.h create mode 100644 src/lib/GZK.cpp create mode 100644 src/lib/GZK.h create mode 100644 src/lib/GammaPP.cpp create mode 100644 src/lib/GammaPP.h create mode 100644 src/lib/ICS.cpp create mode 100644 src/lib/ICS.h create mode 100644 src/lib/Inoue12IROSpectrum.cpp create mode 100644 src/lib/Inoue12IROSpectrum.h create mode 100644 src/lib/Interaction.cpp create mode 100644 src/lib/Interaction.h create mode 100644 src/lib/Logger.cpp create mode 100644 src/lib/Logger.h create mode 100644 src/lib/MathUtils.cpp create mode 100644 src/lib/MathUtils.h create mode 100644 src/lib/NeutronDecay.cpp create mode 100644 src/lib/NeutronDecay.h create mode 100644 src/lib/Nucleus.cpp create mode 100644 src/lib/Nucleus.h create mode 100644 src/lib/Output.cpp create mode 100644 src/lib/Output.h create mode 100644 src/lib/PPP.cpp create mode 100644 src/lib/PPP.h create mode 100644 src/lib/Particle.cpp create mode 100644 src/lib/Particle.h create mode 100644 src/lib/ParticleStack.cpp create mode 100644 src/lib/ParticleStack.h create mode 100644 src/lib/PhotoDisintegration.cpp create mode 100644 src/lib/PhotoDisintegration.h create mode 100644 src/lib/PrecisionTests.cpp create mode 100644 src/lib/PrecisionTests.h create mode 100644 src/lib/PropagationEngine.cpp create mode 100644 src/lib/PropagationEngine.h create mode 100644 src/lib/ProtonPP.cpp create mode 100644 src/lib/ProtonPP.h create mode 100644 src/lib/Randomizer.cpp create mode 100644 src/lib/Randomizer.h create mode 100644 src/lib/Sophia.cpp create mode 100644 src/lib/Sophia.h create mode 100644 src/lib/Stecker16Background.cpp create mode 100644 src/lib/Stecker16Background.h create mode 100644 src/lib/SteckerEBL.cpp create mode 100644 src/lib/SteckerEBL.h create mode 100644 src/lib/TableBackgrounds.cpp create mode 100644 src/lib/TableBackgrounds.h create mode 100644 src/lib/TableFunction.cpp create mode 100644 src/lib/TableFunction.h create mode 100644 src/lib/TableReader.cpp create mode 100644 src/lib/TableReader.h create mode 100644 src/lib/Test.cpp create mode 100644 src/lib/Test.h create mode 100644 src/lib/Thinning.cpp create mode 100644 src/lib/Thinning.h create mode 100644 src/lib/Units.cpp create mode 100644 src/lib/Units.h create mode 100644 src/lib/Utils.cpp create mode 100644 src/lib/Utils.h create mode 100644 src/lib/main.cpp create mode 100644 src/lib/sophia2cpp.f create mode 100644 src/lib/z2t.cpp diff --git a/README.md b/README.md index ffa6ba9..23a8644 100644 --- a/README.md +++ b/README.md @@ -1 +1,14 @@ -# mcray \ No newline at end of file +# mcray +Framework for Monte Carlo simulation of ultra-high energy cosmic rays and electromagnetic cascade propagation. + +### Authors: + Oleg Kalashev and Alexander Korochkin + +### Features + - propagation of protons, neutrons, nuclei, electon-photon cascade and neutrino can be simulated + - support for interactions with arbitray photon background including trajectory simulation for secondary particles, produced in the interactions + - trajectories of individual particles in presence of magnetic field can be calculated + +### Applications + +[CRbeam](src/app/crbeam) - cosmic ray beam simulation \ No newline at end of file diff --git a/src/app/crbeam/CMakeLists.txt b/src/app/crbeam/CMakeLists.txt new file mode 100644 index 0000000..a16621e --- /dev/null +++ b/src/app/crbeam/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required (VERSION 2.6) +project (CRbeam) + +add_definitions(-DUSE_GSL) + +include_directories ("../../lib" "../../OS/include") + +#include_directories(../../external/include ../../external/OS/include) +set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG -O0 -fopenmp") +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG -O0 -fopenmp -pg") +set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wall -O2 -fopenmp") +set(CMAKE_PREFIX_PATH "external") + +# -rdynamic flux is only supported on systems with ELF executable format +IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux") + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -rdynamic") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -rdynamic") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -rdynamic") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -rdynamic") +endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux") + +FIND_LIBRARY(C_LIBRARY c) +FIND_LIBRARY(GSL_LIBRARY gsl) +FIND_LIBRARY(GSLCBLAS_LIBRARY gslcblas) +add_subdirectory (../../lib mcray) + +set(SOURCE_FILES +CmdLine.cpp +CmdLine.h +CRbeam.cpp +CRbeam.h +) + +add_executable(CRbeam ${SOURCE_FILES}) +target_link_libraries(CRbeam mcray ${C_LIBRARY} ${GSL_LIBRARY} ${GSLCBLAS_LIBRARY}) + + diff --git a/src/app/crbeam/CRbeam.cpp b/src/app/crbeam/CRbeam.cpp new file mode 100644 index 0000000..8987c50 --- /dev/null +++ b/src/app/crbeam/CRbeam.cpp @@ -0,0 +1,613 @@ +/* + * CRbeam.cpp + * + * Authors: + * Oleg Kalashev + * Alexandr Korochkin + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <cstdlib> +#include "ParticleStack.h" +#include "Utils.h" +#include "Cosmology.h" +#include "PropagationEngine.h" +#include <iostream> +#include <GZK.h> +#include <Inoue12IROSpectrum.h> +#include "PPP.h" +#include "ProtonPP.h" +#include "TableBackgrounds.h" +#include "ICS.h" +#include "CRbeam.h" +#include "Test.h" +#include "NeutronDecay.h" +#include "Deflection3D.h" +#include "SteckerEBL.h" +#include "CmdLine.h" +#include "Stecker16Background.h" +#include <omp.h> +#include <stdlib.h> +#include "MathUtils.h" + +#ifndef _DEBUG +#include <sys/time.h> +#endif + +using namespace std; +using namespace mcray; +using namespace Utils; +using namespace Backgrounds; +using namespace Interactions; + + +using namespace cors::cmdline; + + +/* + * This is an example of user_main function performing user defined tasks + * the function should be provided by end user + */ +int user_main(int argc, char** argv) { + CRbeam prog(argc, argv); + return prog.run(); +} + +enum ClineParams +{ + PHelp = 1,PLog,PLogFilterThread,PLogLevel,PTrajectoryLog, + PNparticles,PBatchSize, + PRedshift,PFilamentZ,PEnergy,PMinEnergy,PPowerLaw,PMinSourceEnergy,PPrimary,PNoEMcascade,PSourceEvolM, + PBackground,PBackgroundMult,PExtraBackground,PExtraBackgroundPhysical,PEBLCut,PMonoCmb,PFixedCmbT,PExtDeltaZconst,PExtPowerLow,PExtDeltaZexp, + POutput,POverwriteOutput,PThinning,PRescalePPP,PEGMF,PEGMFL,PRandomEGMF,PTurbulentEGMF,PTauPrint,POutputSuffix +}; +#define xstr(s) str(s) +#define str(s) #s +#define M_POINT_SOURCE 1000 + +static CmdInfo commands[] = { + { PHelp, CmdInfo::FLAG_NULL, "-h", "--help", NULL, "Show help information" }, + { PLog, CmdInfo::FLAG_NULL, "-log", "--log", NULL, "Enable logging" }, + { PLogFilterThread, CmdInfo::FLAG_ARGUMENT, "-logT", "--log-thread", "-1", "Log thread filter (set to -1 to disable)" }, + { PLogLevel, CmdInfo::FLAG_ARGUMENT, "-logL", "--log-level", "1", "Log level:\n" + "\tError = 0,\n" + "\tWarning = 1,\n" + "\tMessage = 2,\n" + "\tVerbose = 3" + }, + { PTrajectoryLog, CmdInfo::FLAG_NULL, "-tlog", "--tlog", NULL, "Log particle trajectories" }, + { PNparticles, CmdInfo::FLAG_ARGUMENT, "-N", "--nparticles", "10000", "Number of particles" }, + { PBatchSize, CmdInfo::FLAG_ARGUMENT, "-bN", "--batch", "0", "Number of particles in minibatch (0=auto)" }, + + { PRedshift, CmdInfo::FLAG_ARGUMENT, "-z", "--z", NULL, "Source z (or maximal z if --m_z par is given)" }, + { PFilamentZ, CmdInfo::FLAG_ARGUMENT, "-zf", "--z-filament", "0.", "Filament z (kill protons with z less than this value)" }, + { PEnergy, CmdInfo::FLAG_ARGUMENT, "-E", "--emax", "1e18", "Initial (or maximal for power law) particle energy in eV" }, + { PMinEnergy, CmdInfo::FLAG_ARGUMENT, "-e", "--emin", "1e11", "Minimal energy in eV" }, + { PPowerLaw, CmdInfo::FLAG_ARGUMENT, "-pl", "--power", "0", "if>0 use E^(-power) injection " }, + { PMinSourceEnergy, CmdInfo::FLAG_ARGUMENT, "-es", "--emin_source", "0", "Minimal injection energy in eV (for power law injection only)" }, + + { PPrimary, CmdInfo::FLAG_ARGUMENT, "-p", "--primary", "10", "Primary particle: \n" + "\tElectron = 0,\n" + "\tPositron = 1,\n" + "\tPhoton = 2,\n" + "\tNeutron 9,\n" + "\tProton 10" }, + { PNoEMcascade, CmdInfo::FLAG_NULL, "-nEM", "--noEMcascade", NULL, "don't calculate spectra of electrons and photons" }, + { PSourceEvolM, CmdInfo::FLAG_ARGUMENT, "-m", "--m_z", xstr(M_POINT_SOURCE), "if parameter is set (!=" xstr(M_POINT_SOURCE) "), use continuous source distribution ~(1+z)^m" }, + { PBackground, CmdInfo::FLAG_ARGUMENT, "-b", "--background", "3", "EBL used:\n" + "0 - zero,\n" + "1 - Kneiske best fit (ELMAG),\n" + "2 - Kneiske minimal (ELMAG),\n" + "3 - Inoue 2012 Baseline,\n" + "4 - Inoue 2012 lower limit,\n" + "5 - Inoue 2012 upper limit,\n" + "6 - Franceschini 2008\n" + "7 - Kneiske best fit (digitized)\n" + "8 - Kneiske minimal (digitized)\n" + "9 - Stecker 2005\n" + "10 - Stecker 2016 lower limit,\n" + "11 - Stecker 2016 upper limit,\n" + "12 - Franceschini 2017,\n" + }, + { PBackgroundMult, CmdInfo::FLAG_ARGUMENT, "-bm", "--backgr-mult", "1", "EBL multiplier" }, + { PExtraBackground, CmdInfo::FLAG_ARGUMENT, "-badd", "--add-backgr", "", "Additional matrix background file path (relative to ./tables)"}, + { PExtraBackgroundPhysical, CmdInfo::FLAG_NULL, "-badd_p", "--add-backgr-phys", NULL, "Treat additional background data as physical (not comoving) density" }, + { PEBLCut, CmdInfo::FLAG_ARGUMENT, "-bcut", "--backgr-cut", "100", "Cut EBL above this energy [eV]" }, + { PMonoCmb, CmdInfo::FLAG_NULL, "-mcmb", "--monocmb", NULL, "Use monochromatic 6.3e-4 eV background instead of CMB" }, + { PFixedCmbT, CmdInfo::FLAG_NULL, "-fcmb", "--fixedcmb", NULL, "use CMB with fixed temperature (1+Zmax)2.73K" }, + + { PExtDeltaZconst, CmdInfo::FLAG_ARGUMENT, "-bc", "--eblDeltaZconst", "-1", "EBL extension DeltaZconst param (set to negative to disable EBL extension)" }, + { PExtPowerLow, CmdInfo::FLAG_ARGUMENT, "-bp", "--eblPowerLow", "0", "EBL extension PowerLow param" }, + { PExtDeltaZexp, CmdInfo::FLAG_ARGUMENT, "-be", "--eblDeltaZexp", "1", "EBL extension DeltaZexp param" }, + + { POutput, CmdInfo::FLAG_ARGUMENT, "-o", "--output", NULL, "Output directory path (must not exist, will be created)" }, + { POverwriteOutput, CmdInfo::FLAG_NULL, "-oo", "--overwrite", NULL, "overwrite output dir if exists" }, + { PThinning, CmdInfo::FLAG_ARGUMENT, "-t", "--thinning", "0.9", "Alpha thinning" }, + { PRescalePPP, CmdInfo::FLAG_ARGUMENT, "-pt", "--ppp-thinning", "10", "PPP Rescale Coefficient (double > 0, actual number of secondaries per interaction)" }, + + { PEGMF, CmdInfo::FLAG_ARGUMENT, "-mf", "--EGMF", "1e-15", "Extragalactic magnetic field in Gauss" }, + { PEGMFL, CmdInfo::FLAG_ARGUMENT, "-mfl", "--lEGMF", "1", "Extragalactic magnetic field correlation length in Mpc at z=0" }, + { PRandomEGMF, CmdInfo::FLAG_NULL, "-mfr", "--randomEGMF", NULL, "randomize EGMF (use different EGMF configurations for different initial particles)" }, + { PTurbulentEGMF, CmdInfo::FLAG_NULL, "-mft", "--turbulentEGMF", NULL, "use turbulent EGMF with Kolmogorov spectrum" }, + { PTauPrint, CmdInfo::FLAG_NULL, "-ptau", "--print-tau", NULL, "print tau" }, + { POutputSuffix, CmdInfo::FLAG_ARGUMENT, "-os", "--output-suffix", "", "Output dir name suffix (appended to autogenerated names)"}, + { 0 }, +}; + +class CRbeamLogger : public Logger{ + int fB_slot; + MagneticField* fB; +public: + CRbeamLogger(std::ostream& aOut, int aB_slot=-1, MagneticField* aB=0): + Logger(aOut), + fB_slot(aB_slot), + fB(aB){ + } + virtual void print_data(const Particle &p, std::ostream& aOut){ + Logger::print_data(p, aOut); + MagneticField* pB = fB; + if(fB_slot>=0){ + if(!pB) + pB = (MagneticField*)(p.interactionData[fB_slot]); + } + double Bperp = 0.; + if(pB){ + std::vector<double> B0(3); + pB->GetValueGauss(p.X, p.Time, B0); + Bperp = sqrt(B0[0]*B0[0]+B0[1]*B0[1]); + } + aOut << "\t" << Bperp; + } + + virtual void print_header(std::ostream& aOut){ + Logger::print_header(aOut); + aOut << "\tB_perp/G"; + } +}; + +cosmo_time CRbeam::FilamentFilter::GetMinTravelTimeLeft(const Particle& aParticle) const{ + if (aParticle.ElectricCharge()) + if (aParticle.Time.z() >= fFilamentLocation.z()) + return fFilamentLocation.t() - aParticle.Time.t(); + else if(aParticle.Time.z()<fFilamentLocation.z()*0.9999) + return 0.;// avoid rounding error when + + return Result::GetMinTravelTimeLeft(aParticle); +} + +CRbeam::CRbeam(int argc, char** argv): + fEmin_eV(0), + fEmax_eV(0), + fPowerLaw(0), + fEminSource_eV(0), + fZmax(0), + fZfilament(0), + fAlpha(0), + fPPPrescaleCoef(10), + fNoParticles(0), + fBatchSize(0), + fPrimary(Proton), + fNoEM(false), + fMz(M_POINT_SOURCE), + fBackgroundModel(0), + fEBLmult(1), + fCustomBackground(""), + fCustomBackgroundPhysical(false), + fMonoCMB(false), + fOutputDir(0), + fOverwriteOutput(false), + fFixedCmb(false), + fEGMF(0.), + fLcorEGMF(1.), + fRandomizeEGMF(false), + fTurbulentEGMF(false), + //fMaxDeflection(0.5/180.*M_PI), + fLogging(false), + fTrajectoryLogging(false), + fLogThread(-1), + fLogLevel(WarningLL), + fEBLMaxE(100.), + fTauPrint(false), + cmd(argc,argv,commands) +{ + if( cmd.has_param("-h") ) + { + cmd.printHelp(std::cerr); + exit(255);//enable binary_check + } + fLogging = cmd(PLog); + fTrajectoryLogging = cmd(PTrajectoryLog); + fLogThread = cmd(PLogFilterThread); + fLogLevel = (LogLevel)((int)cmd(PLogLevel)); + fEmin_eV = cmd(PMinEnergy); + fEmax_eV = cmd(PEnergy); + fPowerLaw = cmd(PPowerLaw); + fEminSource_eV = cmd(PMinSourceEnergy); + if(fEminSource_eV<fEmin_eV) + fEminSource_eV=fEmin_eV; + fZmax = cmd(PRedshift); + fAlpha = cmd(PThinning); + fPPPrescaleCoef = cmd(PRescalePPP); + + fEGMF = cmd(PEGMF); + fLcorEGMF = cmd(PEGMFL); + fRandomizeEGMF = cmd(PRandomEGMF); + fTurbulentEGMF = cmd(PTurbulentEGMF); + //fMaxDeflection = cmd(PMaxDeflection); + //fMaxDeflection *= (M_PI/180.); + + fNoParticles = cmd(PNparticles); + fBatchSize = cmd(PBatchSize); + int primary = cmd(PPrimary); + fNoEM = cmd(PNoEMcascade); + fMz = cmd(PSourceEvolM); + fBackgroundModel = cmd(PBackground); + fEBLmult = cmd(PBackgroundMult); + fCustomBackground = cmd(PExtraBackground); + fCustomBackgroundPhysical = cmd(PExtraBackgroundPhysical); + fMonoCMB = cmd(PMonoCmb); + fFixedCmb = cmd(PFixedCmbT); + fOutputDir = cmd(POutput); + fOverwriteOutput = cmd(POverwriteOutput); + fExtDeltaZconst = cmd(PExtDeltaZconst); + fExtPowerLow = cmd(PExtPowerLow); + fExtDeltaZexp = cmd(PExtDeltaZexp); + fEBLMaxE = cmd(PEBLCut); + fTauPrint = cmd(PTauPrint); + fZfilament = cmd(PFilamentZ); + + if(fOutputDir[0]=='\0') + fOutputDir = 0; + + if(primary<Electron || primary>Proton) + Exception::Throw("invalid primary particle type"); + fPrimary = (ParticleType)primary; +} + +int CRbeam::run() +{ + int kAc = 1; + fEBLMaxE *= units.eV; + double Emax = fEmax_eV*units.eV;//eV + if(!cosmology.IsInitialized()) + cosmology.Init(fZmax + 10); + double zMin = 0.; + double logStepK = pow(10,0.05/kAc); + double Emin = fEmin_eV*units.eV; + double EminSource = fEminSource_eV*units.eV; + double alphaThinning = fAlpha;//alpha = 1 conserves number of particles on the average; alpha = 0 disables thinning + std::string outputDir; + + if(fOutputDir) + outputDir = fOutputDir; + else + { + outputDir = Particle::Name(fPrimary); + outputDir += ("_E" + ToString(fEmax_eV) + "_z" + ToString(fZmax) + "_N" + ToString(fNoParticles)) + "_EBL" + ToString(fBackgroundModel); + if(strlen(fCustomBackground)){ + outputDir += 'c'; + } + if(fZfilament>0.) + outputDir += "_zf" + ToString(fZfilament); + if(fEGMF!=0) + { + outputDir += "_B"; + if(fRandomizeEGMF) + outputDir += "rand"; + if(fTurbulentEGMF) + outputDir += "Turb"; + outputDir += ToString(fEGMF); + outputDir += "_Lc"; + outputDir += ToString(fLcorEGMF); + } + else + { + outputDir += "_B0"; + } + if(fNoEM) + outputDir += "_noEM"; + if(fMonoCMB) + outputDir += "_mono"; + if(fFixedCmb) + outputDir += "_fixedCmb"; + if(fPowerLaw>0) + outputDir += ("_p" + ToString(fPowerLaw)); + if(fMz!=M_POINT_SOURCE) + outputDir += ("_m" + ToString(fMz)); + const char* suffix = cmd(POutputSuffix); + outputDir += suffix; + } + std::cout << "output dir: " << outputDir << std::endl; + SmartPtr<RawOutput3D> pOutput = new RawOutput3D(outputDir, fOverwriteOutput, fMz!=M_POINT_SOURCE, fPowerLaw>0, fTrajectoryLogging);//this creates folder outputDir, later we will set output to outputDir/z0 + if(fLogging){ + debug.SetOutputFile(outputDir + "/log.txt"); + debug.EnableTimestamp(); + debug.SetThreadFilter(fLogThread); + debug.SetLevel(fLogLevel); + } + CompoundBackground backgr; + + double cmbTemp = 2.73/Units::phTemperature_mult/units.Eunit; + if(fFixedCmb) + cmbTemp *= (1.+fZmax); + IBackground* b1 = new PlankBackground(cmbTemp, 1e-3*cmbTemp, 1e3*cmbTemp, 0., fZmax + 1., fFixedCmb); + IBackground* b2 = 0; + switch(fBackgroundModel) + { + case 0: + b2 = 0; + break; + case 1: + b2 = new ElmagKneiskeBestFit(); + break; + case 2: + b2 = new ElmagKneiskeMinimal(); + break; + case 3: + b2 = new Inoue12BaselineIROSpectrum(); + break; + case 4: + b2 = new Inoue12LowPop3IROSpectrum(); + break; + case 5: + b2 = new Inoue12UpperPop3IROSpectrum(); + break; + case 6: + b2 = new Franceschini08EBL(); + break; + case 7: + b2 = new Kneiske0309EBL(); + break; + case 8: + b2 = new Kneiske1001EBL(); + break; + case 9: + b2 = new Stecker2005EBL(); + break; + case 10: + b2 = new Stecker16LowerBackground(); + break; + case 11: + b2 = new Stecker16UpperBackground(); + break; + case 12: + b2 = new Franceschini17EBL(); + break; + default: + Exception::Throw("Invalid background model specified"); + } + + if(b2 && fEBLMaxE < b2->MaxE()) + b2 = new CuttedBackground(b2, 0, fEBLMaxE); + + if(b2 && fZmax>b2->MaxZ() && fExtDeltaZconst>=0) + { + b2 = new HighRedshiftBackgrExtension(b2, fExtDeltaZconst, fExtPowerLow, fExtDeltaZexp); + } + + backgr.AddComponent(b1); + if(b2) + { + backgr.AddComponent(b2, fEBLmult); + double dens = BackgroundUtils::CalcIntegralDensity(*b2)*units.cm3; + std::cerr << "EBL integral density [cm^{-3}]:" << dens << std::endl; + std::ofstream backgrOut; + backgrOut.open((outputDir + "/backgr").c_str(),std::ios::out); + BackgroundUtils::Print(b2, 0., backgrOut, 100); + } + if(strlen(fCustomBackground)){ + MatrixBackground* bc = new MatrixBackground(fCustomBackground, fCustomBackground, !fCustomBackgroundPhysical, false); + backgr.AddComponent(bc); + double dens = BackgroundUtils::CalcIntegralDensity(*bc)*units.cm3; + std::cerr << "Custom background integral density [cm^{-3}]: " << dens << std::endl; + std::ofstream backgrOut((outputDir + "/cbackgr").c_str()); + BackgroundUtils::Print(bc, 0., backgrOut, 100); + } + + double stepZ = fZmax<0.05 ? fZmax/2 : 0.025; + double epsRel = 1e-3; + double centralE1 = 6.3e-10/units.Eunit;//6.3e-4eV + double n1 = 413*units.Vunit;//413cm^-3 + ConstFunction k1(centralE1); + ConstFunction c1(n1); + PBackgroundIntegral backgrI; + if(fMonoCMB) + { + backgrI = new MonochromaticBackgroundIntegral(c1, k1, logStepK, epsRel); + } + else + { + backgrI = new ContinuousBackgroundIntegral(backgr, stepZ, logStepK, fZmax, epsRel); + } + + unsigned long int seed = 0; +#ifdef _DEBUG + seed=2015; +#else + { + struct timeval tv; + gettimeofday(&tv, NULL); + seed = tv.tv_sec * 1000 + tv.tv_usec / 1000; + } +#endif + Randomizer rand(seed); + SmartPtr<IFilter> resultFilter = new EnergyBasedFilter(Emin, M_PI); + bool useResultFilterInRuntime = true; + FilamentFilter result(fZfilament, resultFilter, useResultFilterInRuntime); + result.SetAutoFlushInterval(10);//save every 10 particle cascades + result.EnableFlushOnCtrlC();//make sure results are not lost if program is interrupted by Ctrl-C + + result.AddOutput(pOutput); + ParticleStack particles; + PropagationEngine pe(particles, result, rand.CreateIndependent()); + + EnergyBasedThinning thinning(alphaThinning, BlackList); + thinning.EnableFor(Electron); + thinning.EnableFor(Positron); + thinning.EnableFor(Photon); + pe.SetThinning(&thinning); + pe.AddInteraction(new RedShift()); + + if(fNoEM) { + ParticleType discardedParticles[] = {Electron, Positron, Photon, ParticleTypeEOF}; + pe.SetPropagationFilter(new ParticleTypeFilter(discardedParticles)); + } + else + { + pe.AddInteraction(new Interactions::ICS(backgrI, Emin)); + pe.AddInteraction(new Interactions::IcsCEL(backgrI, Emin)); + pe.AddInteraction(new Interactions::GammaPP(backgrI)); + pe.AddInteraction(new PPP(backgrI, fPPPrescaleCoef));//secondaries only + } + pe.AddInteraction(new GZK(backgrI)); + pe.AddInteraction(new NeutronDecay()); + pe.AddInteraction(new ProtonPPcel(backgrI)); + + double Beta = 0.1; + double Alpha = M_PI / 4; + double Theta = 0; + double Phi = 0; + int Bslot = -1; + SmartPtr<MagneticField> mf; + if(fEGMF>0.) { + if (fRandomizeEGMF) { + Deflection3D* defl = new Deflection3D(); + Bslot = defl->MFSlot(); + pe.AddInteraction(defl); + } + else { + mf = fTurbulentEGMF ? ((MagneticField*) new TurbulentMF(rand, fLcorEGMF, fEGMF)) : + ((MagneticField*) new MonochromaticMF(fLcorEGMF, fEGMF, Beta, Alpha, Theta, Phi)); + pe.AddInteraction(new Deflection3D(mf)); + } + } + //// generating initial state + + CosmoTime tEnd; + tEnd.setZ(zMin); + pOutput->SetOutputDir(outputDir + "/z" + ToString(zMin)); + fstream paramsOut((outputDir + "/params").c_str(), std::ios_base::out); + cmd.printParamValues(paramsOut); + result.SetEndTime(tEnd); + CosmoTime tStart; + tStart.setZ(fZmax); + double dt = tEnd.t()-tStart.t(); + double t0 = tStart.t(); + + double normW = 1.; + if(fPowerLaw>0 && fPowerLaw!=1.){//make sure total weight is roughly equal to number of particles + normW = log(Emax/EminSource)*(1.-fPowerLaw)/(pow(Emax,1.-fPowerLaw)-pow(EminSource,1.-fPowerLaw)); + } + SafePtr<CRbeamLogger> tr_logger; + std::ofstream trLogOut; + if(fTrajectoryLogging){ + trLogOut.open((outputDir + "/tr.log").c_str()); + if(!trLogOut.is_open()){ + Exception::Throw("Failed to create " + outputDir + "/tr.log"); + } + + tr_logger = new CRbeamLogger(trLogOut, Bslot, mf); + pe.SetLogger(tr_logger); + fBatchSize = fNoParticles; // this is needed to make sure particle Id's are unique + } + if(fBatchSize==0){ + fBatchSize = omp_get_max_threads()*10; + std::cerr << "using auto batch size " << fBatchSize << std::endl; + } + + std::vector<SafePtr<MagneticField> > fields(fBatchSize); + if(fTauPrint){ + class Kern : public Function{ + PropagationEngine& fPe; + mutable Particle fParticle; + public: + Kern(PropagationEngine& pe, const Particle aParticle): + fPe(pe), + fParticle(aParticle){}; + virtual double f(double t) const{ + fParticle.Time = t; + return fPe.GetInteractionRate(fParticle); + } + }; + CosmoTime tSource(fZmax); + //CosmoTime tEnd(0); + if(fPowerLaw<=0.) + EminSource = Emax; + double step = pow(10.,0.01); + MathUtils mu; + ofstream out_tau((outputDir + "/tau").c_str()); + for (double E=EminSource; E<=Emax; E*=step){ + Particle particle(fPrimary, tSource.z()); + particle.Energy = E; + Kern k(pe, particle); + double tau = mu.Integration_qag(k, tSource.t(),tEnd.t(), 0, 1e-3, 1000); + out_tau << E/units.eV << "\t" << tau << std::endl; + } + out_tau.close(); + } + for(int i=0; i<fNoParticles;) + { + CosmoTime tSource(fZmax); + double weight = 1.; + if(fMz!=M_POINT_SOURCE) + { + double t = t0 + rand.Rand()*dt; + tSource.setT(t); + double z = tSource.z(); + weight = pow(1.+z,fMz); + } + Particle particle(fPrimary, tSource.z()); + if(fPowerLaw<=0.){ + particle.Energy = Emax; + particle.Weight = weight; + } + else{ + particle.Energy = EminSource*pow(Emax/EminSource, rand.Rand());//choose E randomly in log scale + particle.Weight = weight*normW*pow(particle.Energy, 1.-fPowerLaw); + } + + if(fRandomizeEGMF) + { + MagneticField* mf = fTurbulentEGMF ? ((MagneticField*) new TurbulentMF(rand, fLcorEGMF, fEGMF)) : + ((MagneticField*) new MonochromaticMF(rand, fLcorEGMF, fEGMF)); + fields[i%fBatchSize] = mf; //store till the end of current batch + particle.interactionData[Bslot] = mf; + } + particles.AddPrimary(particle); + i++; + if(i%fBatchSize==0 || i==fNoParticles){ + std::cerr << "batch " << (i-1)/fBatchSize + 1 << " of " << ceil(fNoParticles/(double)fBatchSize) << std::endl; + pe.RunMultithread(); + result.Flush(); + if(result.AbortRequested()) + break; //handling Ctrl-C + } + } + std::cout << "output dir: " << outputDir << std::endl; + return 0; +} + +CRbeam::~CRbeam() { + +} + + diff --git a/src/app/crbeam/CRbeam.h b/src/app/crbeam/CRbeam.h new file mode 100644 index 0000000..a905ac2 --- /dev/null +++ b/src/app/crbeam/CRbeam.h @@ -0,0 +1,106 @@ +/* + * CRbeam.h + * + * Authors: + * Oleg Kalashev + * Alexandr Korochkin + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef CRBEAM_H_ +#define CRBEAM_H_ + +#include <cstdlib> +#include <iostream> +#include "ParticleStack.h" +#include "Utils.h" +#include "Cosmology.h" +#include "PropagationEngine.h" +#include "Output.h" +#include "TableBackgrounds.h" +#include "ICS.h" +#include "GammaPP.h" +#include "CmdLine.h" + +using namespace std; +using namespace mcray; +using namespace Utils; +using namespace Backgrounds; +using namespace Interactions; + +class CRbeam { + class FilamentFilter : public Result{ + public: + FilamentFilter(cosmo_time aRedshift, IFilter* aOutputFilter, bool aIsPropagationFilter): + Result(aOutputFilter, aIsPropagationFilter), + fFilamentLocation(aRedshift) + {} + virtual cosmo_time GetMinTravelTimeLeft(const Particle& aParticles) const; + private: + CosmoTime fFilamentLocation; + }; + +public: + CRbeam(int argc, char** argv); + virtual ~CRbeam(); + void Test(); + int run(); +private: + double fEmin_eV; + double fEmax_eV; + double fPowerLaw; + double fEminSource_eV; + double fZmax; + double fZfilament; + double fAlpha; + double fPPPrescaleCoef; + int fNoParticles; + int fBatchSize; + ParticleType fPrimary; + bool fNoEM; + double fMz; + int fBackgroundModel; + double fEBLmult; + const char* fCustomBackground; + bool fCustomBackgroundPhysical; + bool fMonoCMB; + double fExtDeltaZconst; + double fExtPowerLow; + double fExtDeltaZexp; + const char* fOutputDir; + bool fOverwriteOutput; + bool fFixedCmb; + double fEGMF; + double fLcorEGMF; + bool fRandomizeEGMF; + bool fTurbulentEGMF; + bool fLogging; + bool fTrajectoryLogging; + int fLogThread; + LogLevel fLogLevel; + double fEBLMaxE; + bool fTauPrint; + //double fMaxDeflection; + cors::cmdline::CmdLine cmd; +}; + +#endif /* CRBEAM_H_ */ diff --git a/src/app/crbeam/CmdLine.cpp b/src/app/crbeam/CmdLine.cpp new file mode 100644 index 0000000..09fb79f --- /dev/null +++ b/src/app/crbeam/CmdLine.cpp @@ -0,0 +1,332 @@ +/** + * + * CmdLine.cpp + * + * Author: + * Dmitry Ponomarev (Aguacero) <demdxx@gmail.com> + * + * Copyright (c) 2011 Dmitry Ponomarev V. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <stdlib.h> +#include <string.h> +#include <iosfwd> + +#include "CmdLine.h" + +using namespace cors::cmdline; + +// Command argument --- + +CmdArgument::CmdArgument( const char* value ) : value( value ) {} + +CmdArgument::operator CmdArgument::ASCII_TYPE( void ) +{ + return value; +} + +CmdArgument::operator int( void ) +{ + return value!=NULL ? atoi(value) : 0; +} + +CmdArgument::operator float( void ) +{ + return value!=NULL ? atof(value) : 0.; +} + +CmdArgument::operator bool( void ) +{ + return value!=NULL; +} + +// Command Param --- + +CmdParam::CmdParam( const char* value, const CmdInfo* info ) + : CmdArgument( value ), info( info ) {} + +CmdParam::operator CmdArgument::ASCII_TYPE( void ) +{ + if( info && info->flags&CmdInfo::FLAG_ARGUMENT ) { + if( value!=NULL && value[0]!='\0' ) + return value; + return info->default_value!=NULL ? info->default_value : ""; + } + return info ? (value?"Y":"N") : value; +} + +CmdParam::operator int( void ) +{ + if( info && info->flags&CmdInfo::FLAG_ARGUMENT ) { + if( value!=NULL && value[0]!='\0' ) + return atoi(value); + return info->default_value!=NULL ? atoi(info->default_value) : 0; + } + return info ? 1 : 0; +} + +CmdParam::operator double( void ) +{ + if( info && info->flags&CmdInfo::FLAG_ARGUMENT ) { + if( value!=NULL && value[0]!='\0' ) + return atof(value); + return info->default_value!=NULL ? atof(info->default_value) : 0.; + } + return info ? 1. : 0.; +} + +CmdParam::operator bool( void ) +{ + return value!=NULL; +} + +CmdParam::operator CmdParam::CmdInfoPtr( void ) +{ + return info; +} + +const char* CmdParam::key( bool sh ) +{ + const char* n = info ? ( sh ? info->short_name : info->name ) : ""; + return n ? n : ""; +} + +int CmdParam::code( void ) +{ + return info ? info->code : 0; +} + +// Command Line --- + +CmdLine::CmdLine( int argc, char** argv, CmdInfo* commands ) + : _argc(argc), _argv(argv), _commands(commands) {} + +CmdInfo* CmdLine::get_command( const char* name ) +{ + if( _commands ) { + for( int i=0 ; ; i++ ) { + if( _commands[i].code == 0 ) + return NULL; + if( strcmp(_commands[i].short_name,name)==0 || strcmp(_commands[i].name,name)==0 ) + return _commands+i; + } + } + return NULL; +} + +CmdInfo* CmdLine::get_command( int code ) +{ + if( _commands ) { + for( int i=0 ; ; i++ ) { + if( _commands[i].code == 0 ) + return NULL; + if( _commands[i].code == code ) + return _commands+i; + } + } + return NULL; +} + +bool CmdLine::has_param( const char* name ) +{ + if( _argc>1 && _commands ) { + CmdInfo* info = NULL; + for( int subindex = 1 ; subindex<_argc ; subindex++ ) { + info = get_command(_argv[subindex]); + if( !info ) continue; + + if( info->flags&CmdInfo::FLAG_ARGUMENT ) + subindex++; + + if( !info->short_name || strcmp(info->short_name,name)!=0 ) + if( info->name && strcmp(info->name,name)!=0 ) continue; + + return true; + } + } + return false; +} + +CmdParam CmdLine::get_param( const char* name ) +{ + if( _argc>1 && _commands ) { + CmdInfo* info = NULL; + for( int subindex = 1 ; subindex<_argc ; subindex++ ) { + info = get_command(_argv[subindex]); + if( !info ) continue; + + if( info->flags&CmdInfo::FLAG_ARGUMENT ) + subindex++; + + if( !info->short_name || strcmp(info->short_name,name)!=0 ) + if( info->name && strcmp(info->name,name)!=0 ) continue; + + return CmdParam( _argv[subindex], info ); + } + } + return CmdParam(NULL,get_command(name)); +} + +CmdParam CmdLine::get_param( int index ) +{ + if( _argc>1 && _commands ) { + CmdInfo* info = NULL; + for( int subindex = 1 ; subindex<_argc ; subindex++ ) { + info = get_command(_argv[subindex]); + if( !info ) continue; + + if( info->flags&CmdInfo::FLAG_ARGUMENT ) + subindex++; + + if( --index<0 ) + return CmdParam( _argv[subindex], info ); + } + } + return CmdParam(NULL,NULL); +} + +CmdParam CmdLine::get_param_by_code( int code ) +{ + if( _argc>1 && _commands ) { + CmdInfo* info = NULL; + for( int subindex = 1 ; subindex<_argc ; subindex++ ) + { + info = get_command(_argv[subindex]); + if( !info ) continue; + + if( info->flags&CmdInfo::FLAG_ARGUMENT ) + subindex++; + + if( info->code == code) + return CmdParam( _argv[subindex], info ); + } + } + return CmdParam(NULL,get_command(code)); +} + +int CmdLine::get_param_count( void ) +{ + int icount = 0; + + if( _argc>1 && _commands ) { + CmdInfo* info = NULL; + bool b = false; + for( int subindex = 1 ; subindex<_argc ; subindex++ ) { + if( b ) { + icount++; + b = false; + } + else { + info = get_command(_argv[subindex]); + if( !info ) continue; + + if( info->flags&CmdInfo::FLAG_ARGUMENT ) + b = true; + else + icount++; + } + } + } + return icount; +} + +CmdArgument CmdLine::get_argument( int index ) +{ + if( _argc>1 && _commands ) { + CmdInfo* info = NULL; + for( int subindex = 1 ; subindex<_argc ; subindex++ ) { + info = get_command(_argv[subindex]); + if( !info ) { + if( --index<0 ) + return CmdArgument( _argv[subindex] ); + continue; + } + + if( info->flags&CmdInfo::FLAG_ARGUMENT ) + subindex++; + } + } + return CmdArgument(NULL); +} + +int CmdLine::get_argument_count( void ) +{ + int icount = 0; + + if( _argc>1 && _commands ) { + CmdInfo* info = NULL; + for( int subindex = 1 ; subindex<_argc ; subindex++ ) { + info = get_command(_argv[subindex]); + if( !info ) { icount++; continue; } + + if( info->flags&CmdInfo::FLAG_ARGUMENT ) + subindex++; + } + } + return icount; +} + +CmdParam CmdLine::operator[]( const char* name ) +{ + return get_param(name); +} + +CmdParam CmdLine::operator[]( int index ) +{ + return get_param(index); +} + +CmdParam CmdLine::operator()( int code ) +{ + return get_param_by_code(code); +} + +CmdLine::operator int( void ) +{ + return get_param_count(); +} + +void CmdLine::printHelp(std::ostream& aOut) { + aOut << "Command line parameters:" << std::endl + << "--------------------------" << std::endl; + + for (int i = 0; _commands[i].code > 0; i++) { + aOut << _commands[i].short_name << " or " << _commands[i].name + << "\n\t\t" << _commands[i].description << std::endl; + if (_commands[i].default_value) + aOut << "\t\tdefault value:\t" << _commands[i].default_value << std::endl; + } +} + +void CmdLine::printParamValues(std::ostream& aOut) +{ + aOut << "# Command line:\n#"; + for(int i=0; i<_argc; i++){ + aOut << " " << _argv[i]; + } + aOut << "\n#\n# Param\tValue\n"; + //start from i = 1 to skip "--help" + for (int i = 1; _commands[i].code > 0; i++) { + const char* val = (const char*)get_param_by_code(i+1); + aOut << (_commands[i].name) << "\t" << val << "\n"; + } + aOut.flush(); +} \ No newline at end of file diff --git a/src/app/crbeam/CmdLine.h b/src/app/crbeam/CmdLine.h new file mode 100644 index 0000000..4192fc8 --- /dev/null +++ b/src/app/crbeam/CmdLine.h @@ -0,0 +1,117 @@ +/** + * + * CmdLine.h + * + * Author: + * Dmitry Ponomarev (Aguacero) <demdxx@gmail.com> + * + * Copyright (c) 2011 Dmitry Ponomarev V. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include <ostream> + +namespace cors +{ + namespace cmdline + { + struct CmdInfo + { + enum { + FLAG_NULL = 0, + FLAG_ARGUMENT = 0x01, + }; + + int code; + char flags; + const char* short_name; + const char* name; + const char* default_value; + const char* description; + }; + + class CmdArgument + { + public: + typedef const char* ASCII_TYPE; + + const char* value; + + public: + CmdArgument( const char* value ); + + operator ASCII_TYPE( void ); + operator int( void ); + operator float( void ); + operator bool( void ); + }; + + class CmdParam : public CmdArgument + { + public: + typedef const CmdInfo* CmdInfoPtr; + + const CmdInfo* info; + + public: + CmdParam( const char* value, const CmdInfo* info ); + + operator ASCII_TYPE( void ); + operator int( void ); + operator double( void ); + operator bool( void ); + operator CmdInfoPtr( void ); + + const char* key( bool sh = false ); + int code( void ); + }; + + class CmdLine + { + protected: + int _argc; + char** _argv; + CmdInfo* _commands; + public: + CmdLine( int argc, char** argv, CmdInfo* commands ); + CmdInfo* get_command( const char* name ); + CmdInfo* get_command( int code ); + + bool has_param( const char* name ); + CmdParam get_param( const char* name ); + CmdParam get_param( int index ); + CmdParam get_param_by_code( int code ); + int get_param_count( void ); + + CmdArgument get_argument( int index ); + int get_argument_count( void ); + + CmdParam operator[]( const char* name ); + CmdParam operator[]( int index ); + CmdParam operator()( int index ); + operator int( void ); + void printHelp(std::ostream& aOut); + void printParamValues(std::ostream& aOut); + }; + + } +} diff --git a/src/app/crbeam/README.md b/src/app/crbeam/README.md new file mode 100644 index 0000000..2a16963 --- /dev/null +++ b/src/app/crbeam/README.md @@ -0,0 +1,172 @@ +# CRbeam + +Monte Carlo simulation of the the cosmic ray and EM cascade beam propagation. + +### Authors: + Oleg Kalashev and Alexander Korochkin + +### Build: + + git clone git@github.com:okolo/mcray.git + cd mcray + cmake src/app/crbeam + make + +### Usage example: + + ./CRbeam -z 0.43 --emax 1e18 --emin 3e11 -N 100000 -p 10 -b 12 -mf 1e-17 -mft -mfr --backgr-mult 3.35 --lEGMF 0.01 + +### Command line parameters: + +-------------------------- +-h or --help + Show help information + +-log or --log + Enable logging + +-logT or --log-thread + Log thread filter (set to -1 to disable) + default value: -1 + +-logL or --log-level + Log level: + Error = 0, + Warning = 1, + Message = 2, + Verbose = 3 + default value: 1 + +-tlog or --tlog + Log particle trajectories + +-N or --nparticles + Number of particles + default value: 10000 + +-bN or --batch + Number of particles in minibatch (0=auto) + default value: 0 + +-z or --z + Source z (or maximal z if --m_z par is given) + +-zf or --z-filament + Filament z (kill protons with z less than this value) + default value: 0. + +-E or --emax + Initial (or maximal for power law) particle energy in eV + default value: 1e18 + +-e or --emin + Minimal energy in eV + default value: 1e11 + +-pl or --power + if>0 use E^(-power) injection + default value: 0 + +-es or --emin_source + Minimal injection energy in eV (for power law injection only) + default value: 0 + +-p or --primary + Primary particle: + Electron = 0, + Positron = 1, + Photon = 2, + Neutron 9, + Proton 10 + default value: 10 + +-nEM or --noEMcascade + don't calculate spectra of electrons and photons + +-m or --m_z + if parameter is set (!=1000), use continuous source distribution ~(1+z)^m + default value: 1000 + +-b or --background + EBL used: +0 - zero, +1 - Kneiske best fit (ELMAG), +2 - Kneiske minimal (ELMAG), +3 - Inoue 2012 Baseline, +4 - Inoue 2012 lower limit, +5 - Inoue 2012 upper limit, +6 - Franceschini 2008 +7 - Kneiske best fit (digitized) +8 - Kneiske minimal (digitized) +9 - Stecker 2005 +10 - Stecker 2016 lower limit, +11 - Stecker 2016 upper limit, +12 - Franceschini 2017; +default value: 3 + +-bm or --backgr-mult + EBL multiplier + default value: 1 + +-badd or --add-backgr + Additional matrix background file path (relative to ./tables) + +-badd_p or --add-backgr-phys + Treat additional background data as physical (not comoving) density + +-bcut or --backgr-cut + Cut EBL above this energy [eV] + default value: 100 + +-mcmb or --monocmb + Use monochromatic 6.3e-4 eV background instead of CMB + +-fcmb or --fixedcmb + use CMB with fixed temperature (1+Zmax)2.73K + +-bc or --eblDeltaZconst + EBL extension DeltaZconst param (set to negative to disable EBL extension) + default value: -1 + +-bp or --eblPowerLow + EBL extension PowerLow param + default value: 0 + +-be or --eblDeltaZexp + EBL extension DeltaZexp param + default value: 1 + +-o or --output + Output directory path (must not exist, will be created) + +-oo or --overwrite + overwrite output dir if exists + +-t or --thinning + Alpha thinning + default value: 0.9 + +-pt or --ppp-thinning + PPP Rescale Coefficient (double > 0, actual number of secondaries per interaction) + default value: 10 + +-mf or --EGMF + Extragalactic magnetic field in Gauss + default value: 1e-15 + +-mfl or --lEGMF + Extragalactic magnetic field correlation length in Mpc at z=0 + default value: 1 + +-mfr or --randomEGMF + randomize EGMF (use different EGMF configurations for different initial particles) + +-mft or --turbulentEGMF + use turbulent EGMF with Kolmogorov spectrum + +-ptau or --print-tau + print tau + +-os or --output-suffix + Output dir name suffix (appended to autogenerated names) + default value: diff --git a/src/lib/Background.cpp b/src/lib/Background.cpp new file mode 100644 index 0000000..6a85bcd --- /dev/null +++ b/src/lib/Background.cpp @@ -0,0 +1,1165 @@ +/* + * Background.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#include <math.h> +#include <fstream> +#include <iostream> +#include <sstream> +#include <iterator> +#include <gsl/gsl_sf_erf.h> + +#include "Background.h" +#include "Utils.h" +#include "TableFunction.h" +#include "PrecisionTests.h" + + +namespace mcray { + +using namespace std; + +double BackgroundUtils::CalcIntegralDensity(const IBackground& aBackground, double aRelError) +{ + ASSERT_VALID_NO(aBackground.MinE()); + ASSERT_VALID_NO(aBackground.MaxE()); + double x1 = log(aBackground.MinE()); + double x2 = log(aBackground.MaxE()); + MathUtils math; + gsl_function f; + f.function = DensityCalculator; + f.params = (void*)(&aBackground); + return math.Integration_qag(f, x1, x2, 0, aRelError, 1000); +} + +void BackgroundUtils::Print(const IBackground* aBackground, double aZ, std::ostream& aOut, int nIntervals) +{ + class Spec : public Function + { + const IBackground* fBackground; + double fZ; + virtual double f(double aE_eV) const + { + return fBackground->n(aE_eV*units.eV, fZ)*units.cm3; + } + virtual double Xmin() const {return fBackground->MinE()/units.eV; } + virtual double Xmax() const {return fBackground->MaxE()/units.eV; } + public: + Spec(const IBackground* aBackground, double aZ):fBackground(aBackground), fZ(aZ){} + }; + Spec spec(aBackground, aZ); + spec.Print(aOut, nIntervals, true); +} + +double BackgroundUtils::DensityCalculator (double x, void * params) +{ + IBackground* pBackgr = (IBackground*)params; + return pBackgr->n(exp(x),0); +} + +void BackgroundUtils::UnitTest(const IBackground& aBackground, int aPointsZ, int aPointsE) +{ + double zMax=aBackground.MaxZ(); + if(zMax==0) + aPointsZ = 1; + for(int iZ=0; iZ<aPointsZ; iZ++){ + double z = zMax/aPointsZ*iZ; + ofstream out((aBackground.Name() + "z" + ToString(z)).c_str()); + Print(&aBackground, z, out, aPointsE); + } + std::cerr << "density " << CalcIntegralDensity(aBackground)*units.cm3 << std::endl; +} + +TableBackground::TableBackground(std::string aName): + fZtoIndex(0), + fMinE(DBL_MAX), + fMaxE(0), + fName(aName) +{ + +} + +void TableBackground::Init(std::string aDir, const char** aFileList, bool aIsLogscaleY) +{ + //the files should be ordered increasingly? + //x scale should by monotonic with increasing order + aDir = TABLES_DIR + aDir; + double lastZ = -1; + for(int iZ=0; aFileList[iZ]; iZ++) + { + double z = -1.; + z = atof(aFileList[iZ]); + if(z<0) + throw "Invalid table background file name"; + if(z<lastZ) + throw "Invalid table background file order"; + + std::ifstream file; + file.open((aDir + DIR_DELIMITER_STR + aFileList[iZ]).c_str(), std::ios::in); + if(!file.is_open()) + Exception::Throw("Failed to open " + aDir + DIR_DELIMITER_STR + aFileList[iZ]); + TableReader* reader = new TableReader(file, 2); + Vector& x = reader->getColumn(0); + Vector& y = reader->getColumn(1); + int iMax = x.size(); + for(int iX=0; iX<iMax; iX++) + {//TODO add support of arbitrary ordered data + double E = recordToE(x[iX],z); + if(E<fMinE) + fMinE=E; + else if(E>fMaxE) + fMaxE=E; + } + double leftValue = aIsLogscaleY?(y[0]-1000):0; + double rightValue = aIsLogscaleY?(y[y.size()-1]-1000):0; + + GSLTableFunc* f = new GSLTableFunc(reader,gsl_interp_linear,leftValue,rightValue); + fData.push_back(f); + fZ.push_back(z); + fZindex.push_back((double)iZ); + } + fZtoIndex = new GSLTableFunc(fZ, fZindex, gsl_interp_linear); +} + +TableBackground::~TableBackground() { + delete fZtoIndex; + for(int i = fData.size()-1; i>=0; i--) + delete fData[i]; +} + +MatrixBackground::MatrixBackground(std::string aName, std::string aTableFile, bool aIsComoving, bool aExtendToZero): + fZtoIndex(0), + fLogEtoIndex(0), + fName(aName), + fExtendToZero(aExtendToZero), + fIsComoving(aIsComoving), + fBuffer(0) +{ + const size_t bufSize = 1048576;//1M + fBuffer = new char[bufSize]; + std::istream_iterator<double> eos; + aTableFile = TABLES_DIR + aTableFile; + std::ifstream file(aTableFile.c_str()); + + if(!file.good()) + Exception::Throw("Invalid background table file " + aTableFile); + file.getline(fBuffer, bufSize); + std::istringstream ist(fBuffer); + std::istream_iterator<double> iit (ist); + for(; iit!=eos; iit++) + { + fZ.push_back(*iit); + fZindex.push_back(fZ.size()-1); + } + double Epriv = 0; + while(!file.eof()) + { + file.getline(fBuffer, bufSize); + std::istringstream ist(fBuffer); + std::istream_iterator<double> iit (ist); + if(iit==eos) + break; + double E = *iit; + if(E<=0 || E<Epriv) + Exception::Throw("Invalid background table file " + aTableFile + ": invalid energy value in line " + + ToString(fLogEdN_dE.size()+1)); + Epriv = E; + fLogE.push_back(log(E*1e-6/units.Eunit)); + fLogEindex.push_back(fLogE.size()-1); + std::vector<double>* pN = new std::vector<double>(); + fLogEdN_dE.push_back(pN); + for(iit++; iit!=eos; iit++) + { + double dN_dE = *iit; + if(dN_dE<0) + Exception::Throw("Invalid background table file " + aTableFile + ": line " + + ToString(fLogEdN_dE.size()+1) + " contains " + ToString(pN->size()+1) + + " negative value"); + pN->push_back(dN_dE>0?log(E*dN_dE*units.Vunit):-700.); + } + if(pN->size()!=fZ.size()) + Exception::Throw("Invalid background table file " + aTableFile + ": line " + + ToString(fLogEdN_dE.size()+1) + " contains " + ToString(pN->size()+1) + + " records while " + ToString(fZ.size()+1) + " expected"); + } + fZtoIndex = new GSLTableFunc(fZ, fZindex, gsl_interp_linear, aExtendToZero ? 0. : -1., -1.); + fLogEtoIndex = new GSLTableFunc(fLogE, fLogEindex, gsl_interp_linear, -1., -1.); + fMinE = exp(fLogE[0]); + fMaxE = exp(*(fLogE.end()-1)); + delete fBuffer; + fBuffer = 0; +} + +MatrixBackground::~MatrixBackground() +{ + delete fBuffer; + delete fZtoIndex; + delete fLogEtoIndex; + for(std::vector<Vector*>::iterator it = fLogEdN_dE.begin(); it != fLogEdN_dE.end(); it++) + delete (*it); +} + +double MatrixBackground::n(double aE, double aZ) const +{ + double logE = log(aE); + double indexE = fLogEtoIndex->f(logE); + if(indexE<0.) + return 0.; + double indexZ = fZtoIndex->f(aZ); + if(indexZ<0.) + return 0.; + int iE = indexE; + double fracE = indexE-iE; + int iZ = indexZ; + double fracZ = indexZ-iZ; + double result = fLogEdN_dE[iE]->at(iZ)*(1.-fracZ)*(1.-fracE); + if(fracZ>0) + result += (fLogEdN_dE[iE]->at(iZ+1)*fracZ*(1.-fracE)); + if(fracE>0) + { + result += (fLogEdN_dE[iE+1]->at(iZ)*(1.-fracZ)*fracE); + if(fracZ>0) + result += (fLogEdN_dE[iE+1]->at(iZ+1)*fracZ*fracE); + } + result = exp(result); + if(fIsComoving) + { + double z1=1.+aZ; + result *= (z1*z1*z1); + } + return result; +} + +PlankBackground::PlankBackground(double aT0, double aEmin, double aEmax, double aMinZ, double aMaxZ, bool aFixedTemperature): + fT0(aT0), + fMinE(aEmin), + fMaxE(aEmax), + fMinZ(aMinZ), + fMaxZ(aMaxZ), + fFixedTemperature(aFixedTemperature) +{ + ASSERT(aEmin<aEmax); + ASSERT(aMinZ<aMaxZ); + fName = "PlankT=" + ToString(aT0*units.Eunit*Units::phTemperature_mult) + "K"; +} + +double PlankBackground::n(double aE, double aZ) const +{ + double gamma=aE/fT0; + if(!fFixedTemperature) + gamma /= (1.0+aZ); + if(gamma<700) + { + double ex_1 = (gamma > 1e-2) ? exp(gamma)-1.0 : gamma*(1.+ 0.5*gamma*(1. + 0.333333333333333*gamma*(1. + 0.25*gamma))); + return aE*aE*aE/9.869604401089/ex_1;//that is E^3/Pi^2/(...) + } + return 0; +} + + /*! + @param[in] aE background photons energy central value (internal units) + @param[in] aSigma Gaussian distribution sigma (internal units) + @param[in] aWidth width at which the distribution is cut (in units of sigma) + @param[in] aConcentration integral concentration of photons (internal units) + @param[in] aMinZ minimal background red shift + @param[in] aMaxZ maximal background red shift + */ +GaussianBackground::GaussianBackground(double aE, double aSigma, double aWidth, + double aConcentration, double aMinZ, double aMaxZ): + fE0(aE), fSigma(aSigma), fMinZ(aMinZ), fMaxZ(aMaxZ), fWidth(aWidth) +{ + fMinE = fE0 - 0.5*fSigma*aWidth; + fMaxE = fE0 + 0.5*fSigma*aWidth; + fNorm = 1./fSigma/sqrt(2.*M_PI); + double mult = 1./sqrt(2.)/fSigma; + double conc = 0.5*(gsl_sf_erf(mult*(fMaxE-fE0))-gsl_sf_erf(mult*(fMinE-fE0))); + fNorm *= aConcentration/conc; + fName = "Gaussian ( E0 = " + ToString(fE0*units.Eunit*1e6) + " eV; sigma/E0 = " + + ToString(fSigma/fE0) + "; width/sigma = " + ToString(aWidth) + + "; n = " + ToString(aConcentration/units.Vunit) + "cm^-3 )"; +} + +double GaussianBackground::n(double aE, double aZ) const +{ + double ratio = fabs((aE-fE0)/fSigma); + if(ratio>fWidth) + return 0.; + return fNorm*exp(-0.5*ratio*ratio)*aE; +} + +double TableBackground::n(double aE, double aZ) const +{ + ASSERT(fMaxE>fMinE);//make sure Init() method was called prior + int iZ = (int)fZtoIndex->f(aZ); + if(iZ<0) + return 0; + double x = EtoRecordX(aE,aZ); + double leftY = fData[iZ]->f(x); + double y=0.; + if(iZ==((int)fZ.size())-1) + y = leftY; + else + { + double rightY = fData[iZ+1]->f(x); + double zL = fZ[iZ]; + double zR = fZ[iZ+1]; + y = leftY + (rightY-leftY)/(zR-zL)*(aZ-zL); + } + return recordToN(x, y, aZ); +} + +double TableBackground::MinE() const +{ + ASSERT(fMaxE>fMinE);//make sure Init() method was called prior + return fMinE; +} + +double TableBackground::MaxE() const +{ + ASSERT(fMaxE>fMinE);//make sure Init() method was called prior + return fMaxE; +} + +double TableBackground::MinZ() const +{ + ASSERT(fMaxE>fMinE);//make sure Init() method was called prior + return fZ[0]; +} + +double TableBackground::MaxZ() const +{ + ASSERT(fMaxE>fMinE);//make sure Init() method was called prior + return fZ[fZ.size()-1]; +} + +void CompoundBackground::AddComponent(IBackground* aBackground, double aWeight) +{ + fBackgrounds.push_back(aBackground); + fWeights.push_back(aWeight); +} + +double CompoundBackground::n(double aE, double aZ) const +{ + double sum = 0.; + vector<double>::const_iterator wit = fWeights.begin(); + for(vector<IBackground*>::const_iterator it = fBackgrounds.begin(); it != fBackgrounds.end(); it++, wit++) + { + const IBackground* b = *it; + double weight = *wit; + if(aE>=b->MinE() && aE<=b->MaxE() && aZ>=b->MinZ() && aZ<=b->MaxZ()) + sum += weight*b->n(aE,aZ); + } + return sum; +} + +std::string CompoundBackground::Name() const +{ + std::string result = ""; + for(vector<IBackground*>::const_iterator it = fBackgrounds.begin(); it != fBackgrounds.end(); it++) + { + const IBackground* b = *it; + if(result.length()>0) + result = result + " + " + b->Name(); + else + result = b->Name(); + } + return result; +} + +double CompoundBackground::MinE() const +{ + double result = DBL_MAX; + for(vector<IBackground*>::const_iterator it = fBackgrounds.begin(); it != fBackgrounds.end(); it++) + { + const IBackground* b = *it; + double min = b->MinE(); + if(result>min) + result = min; + } + return result; +} + +double CompoundBackground::MaxE() const +{ + double result = 0; + for(vector<IBackground*>::const_iterator it = fBackgrounds.begin(); it != fBackgrounds.end(); it++) + { + const IBackground* b = *it; + double max = b->MaxE(); + if(result<max) + result = max; + } + return result; +} + +double CompoundBackground::MinZ() const +{ + double result = DBL_MAX; + for(vector<IBackground*>::const_iterator it = fBackgrounds.begin(); it != fBackgrounds.end(); it++) + { + const IBackground* b = *it; + double min = b->MinZ(); + if(result>min) + result = min; + } + return result; +} + +double CompoundBackground::MaxZ() const +{ + double result = 0; + for(vector<IBackground*>::const_iterator it = fBackgrounds.begin(); it != fBackgrounds.end(); it++) + { + const IBackground* b = *it; + double max = b->MaxZ(); + if(result<max) + result = max; + } + return result; +} + +CompoundBackground::~CompoundBackground() +{ + for(vector<IBackground*>::iterator it = fBackgrounds.begin(); it != fBackgrounds.end(); it++) + delete *it; +} + +BackgroundIntegral* ContinuousBackgroundIntegral::Clone() const +{ + return new ContinuousBackgroundIntegral(*this); +} + +ContinuousBackgroundIntegral::ContinuousBackgroundIntegral(const ContinuousBackgroundIntegral& aBackgroundIntegral): + fStepZ(aBackgroundIntegral.fStepZ), + fLogStepK(aBackgroundIntegral.fLogStepK), + fZmaxLimit(aBackgroundIntegral.fZmaxLimit), + fMaxK(aBackgroundIntegral.fMaxK), + fMinK(aBackgroundIntegral.fMinK), + fEpsRel(aBackgroundIntegral.fEpsRel), + fBinningK(aBackgroundIntegral.fBinningK), + fEnableDebugOutput(aBackgroundIntegral.fEnableDebugOutput) +{ + int size = aBackgroundIntegral.fIntegrals.size(); + for(int iZ=0; iZ<size; iZ++) + { + fIntegrals.push_back(aBackgroundIntegral.fIntegrals[iZ]->Clone()); + } +} + +ContinuousBackgroundIntegral::ContinuousBackgroundIntegral(IBackground& aBackground, double aStepZ, double aLogStepK, double aZmaxLimit, double aEpsRel): +fStepZ(aStepZ), +fLogStepK(aLogStepK), +fZmaxLimit(aZmaxLimit), +fEpsRel(aEpsRel), +fEnableDebugOutput(false) +{ + ASSERT(fEpsRel<=0.1); + if(aLogStepK<=1) + Exception::Throw("invalid BackgroundIntegral::BackgroundIntegral argument aLogStepE"); + fMaxK = aBackground.MaxE(); + fMinK = aBackground.MinE(); + if(aBackground.MaxZ()<fZmaxLimit) + fZmaxLimit=aBackground.MaxZ(); + int nIntervalsZ = (int)(fZmaxLimit/fStepZ + 1.0); + ASSERT(nIntervalsZ>=0); + fStepZ = (nIntervalsZ>0)?(fZmaxLimit/((double)nIntervalsZ)):0; + int nIntervalsE = (int)(log10(fMaxK/fMinK)/log10(fLogStepK) + 0.5); + if(nIntervalsE<10) + nIntervalsE = 2; + fLogStepK = pow(fMaxK/fMinK, 1./nIntervalsE); + if(fLogStepK<=1) + Exception::Throw("invalid BackgroundIntegral::BackgroundIntegral argument aBackground (zero energy range)"); + + {double k=fMinK; + for(int iK=0; iK<=nIntervalsE; iK++, k*=fLogStepK) + {//build energy binning + fBinningK.push_back(k); + }} + Kern kern(aBackground); + {for(int iZ=0; iZ<=nIntervalsZ; iZ++) + { + fData.push_back(new Vector(nIntervalsE+1,0.)); + double z=iZ*fStepZ; + kern.Z = z; + double k=fMaxK/fLogStepK; + double sum = 0.; + (*fData[iZ])[nIntervalsE]=0.;//I(k_max)=0 + for(int iK=nIntervalsE-1; iK>=0; iK--,k/=fLogStepK) + { + double I = math.Integration_qag(kern, k, k*fLogStepK, 0, fEpsRel, (int)(1./fEpsRel)); + ASSERT_VALID_NO(I); + sum += I; + (*fData[iZ])[iK]=sum; + } + }} + {for(int iZ=0; iZ<=nIntervalsZ; iZ++) + { + GSLTableFunc* func = new GSLTableFunc(fBinningK, *fData[iZ], new LogScale(), new LogScale(), gsl_interp_linear); + func->SetAutoLimits();//for k<k_min I(k)=I(k_min); for k>k_max I(k)=I(k_max)=0 + fIntegrals.push_back(func); + }} +} + +double ContinuousBackgroundIntegral::Kern::f(double aX) const +{ + // TODO think about switching to log scale integration + return fBackground.n(aX, Z)/(aX*aX*aX); +} + +ContinuousBackgroundIntegral::RateSKern::RateSKern(const Function& aBackrgoundIntegral, const Function& aSigma, const Particle& aParticle) : + fBackrgoundIntegral(aBackrgoundIntegral),fSigma(aSigma) +{ + fM2 = aParticle.Mass(); + fM2*=fM2; + fMaxEmult = 2.0*aParticle.Energy*(1.-aParticle.beta()); + if(fMaxEmult>0) + fMaxEmult = 1./fMaxEmult; + else + fMaxEmult = -1.; + + fMult = 0.5/aParticle.Energy/(1.+aParticle.beta()); +} +//(s-m^2) sigma(s) I((s-m^2)/2/E/(1+beta),z) +double ContinuousBackgroundIntegral::RateSKern::f(double s) const +{ + double bi = fBackrgoundIntegral((s-fM2)*fMult); + if(fMaxEmult>0) + { + bi -= fBackrgoundIntegral((s-fM2)*fMaxEmult); + } + return fSigma(s)*(s-fM2)*bi; +} + +ContinuousBackgroundIntegral::RateKKern::RateKKern(const Function& aBackrgoundIntegral, const Function& aSigma, const Particle& aParticle) : + fBackrgoundIntegral(aBackrgoundIntegral),fSigma(aSigma) +{ + double m = aParticle.Mass(); + + fMaxEmult = aParticle.Energy*(1.-aParticle.beta()); + if(fMaxEmult>0) + fMaxEmult = m/fMaxEmult; + else + fMaxEmult = -1.; + + fMult = m/aParticle.Energy/(1.+aParticle.beta()); +} + +double ContinuousBackgroundIntegral::RateKKern::f(double k) const +{ + double bi = fBackrgoundIntegral(k*fMult); + if(fMaxEmult>0) + { + bi -= fBackrgoundIntegral(k*fMaxEmult); + } + return fSigma(k)*k*bi; +} + +ContinuousBackgroundIntegral::RateSKernZ::RateSKernZ(const Function& aBackrgoundIntegralZ1, const Function& aBackrgoundIntegralZ2, double aZ1, double aZ2, const Function& aSigma, const Particle& aParticle): + RateSKern(aBackrgoundIntegralZ1, aSigma, aParticle), + fBackrgoundIntegralZ1(aBackrgoundIntegralZ1), + fBackrgoundIntegralZ2(aBackrgoundIntegralZ2) +{ + //initializing linear interpolation coefficients + fCoef2 = (aParticle.Time.z()-aZ1)/(aZ2-aZ1); + fCoef1 = 1.-fCoef2; +} + +//(s-m^2) sigma(s) I((s-m^2)/2/E/(1+beta),z) +double ContinuousBackgroundIntegral::RateSKernZ::f(double s) const +{ + double k = (s-fM2)*fMult; + double bi1 = fBackrgoundIntegralZ1(k); + double bi2 = fBackrgoundIntegralZ2(k); + if(fMaxEmult>0) + { + k = (s-fM2)*fMaxEmult; + bi1 -= fBackrgoundIntegralZ1(k); + bi2 -= fBackrgoundIntegralZ2(k); + } + double bi = fCoef1*bi1 + fCoef2*bi2; + double result = fSigma(s)*(s-fM2)*bi; + //std::cout.precision(15); + //std::cout << s << "\t" << result << std::endl; + return result; +} + +ContinuousBackgroundIntegral::RateKKernZ::RateKKernZ(const Function& aBackrgoundIntegralZ1, const Function& aBackrgoundIntegralZ2, double aZ1, double aZ2, const Function& aSigma, const Particle& aParticle): + RateKKern(aBackrgoundIntegralZ1, aSigma, aParticle), + fBackrgoundIntegralZ1(aBackrgoundIntegralZ1), + fBackrgoundIntegralZ2(aBackrgoundIntegralZ2) +{ + //initializing linear interpolation coefficients + fCoef2 = (aParticle.Time.z()-aZ1)/(aZ2-aZ1); + fCoef1 = 1.-fCoef2; +} + +double ContinuousBackgroundIntegral::RateKKernZ::f(double k) const +{ + double eps = k*fMult; + double bi1 = fBackrgoundIntegralZ1(eps); + double bi2 = fBackrgoundIntegralZ2(eps); + if(fMaxEmult>0) + { + eps = k*fMaxEmult; + bi1 -= fBackrgoundIntegralZ1(eps); + bi2 -= fBackrgoundIntegralZ2(eps); + } + double bi = fCoef1*bi1 + fCoef2*bi2; + double result = fSigma(k)*k*bi; + return result; +} + +double ContinuousBackgroundIntegral::GetRateAndSampleS(const Function& aSigma, const Particle& aParticle, double aRand, double& aS, double aAbsError) +{ + ASSERT(aRand>0 || aRand<1); + double SminSigma = aSigma.Xmin(); + ASSERT_VALID_NO(SminSigma); + double beta = aParticle.beta(); + double m=aParticle.Mass(); + double Smax = m*m+2.*aParticle.Energy*fMaxK*(1.+beta); + double SminKinematic = m*m+2.*aParticle.Energy*fMinK*(1.-beta); + double Smin = SminKinematic<SminSigma ? SminSigma : SminKinematic; + if(aSigma.Xmax()<Smax) + Smax=aSigma.Xmax(); + if(Smax<=Smin) + return 0.; + + double z = aParticle.Time.z(); + int iZ= fStepZ>0 ? ((int)(z/fStepZ)):0; + //int maxIntervals = (int)(0.1/fEpsRel + 10.5); + + //int nStepsS = (int)(log10(Smax/Smin)/log10(fLogStepK)+0.5); + //if(nStepsS<5) + // nStepsS = 5; + //double stepS = pow(Smax/Smin,1./nStepsS); + double totalRate = 0.;//rate times 8*E*beta + + bool zDependenceExists = (iZ<(int)fIntegrals.size()-1); + SafePtr<RateSKern> kern; + if(zDependenceExists) + kern = new RateSKernZ(*fIntegrals[iZ], *fIntegrals[iZ + 1], iZ * fStepZ, (iZ + 1) * fStepZ, aSigma, aParticle); + else + kern = new RateSKern(*fIntegrals[iZ], aSigma, aParticle); + if(MathUtils::SampleLogDistribution(*kern, aRand, aS, totalRate, Smin, Smax, fEpsRel)) + { + return totalRate/8./aParticle.Energy/aParticle.Energy/aParticle.beta(); + } + aS=0; + return 0; +} + +double ContinuousBackgroundIntegral::GetRateAndSampleK(const Function& aSigmaK, const Particle& aParticle, double aRand, double&aK, double aAbsError) +{ + ASSERT(aRand>0 || aRand<1); + double KminSigma = aSigmaK.Xmin(); + ASSERT_VALID_NO(KminSigma); + double beta = aParticle.beta(); + double m=aParticle.Mass(); + double Kmax = aParticle.Energy * fMaxK * (1. + beta)/m; + double KminKinematic = aParticle.Energy * fMinK * (1. - beta)/m; + double Kmin = KminKinematic < KminSigma ? KminSigma : KminKinematic; + if(aSigmaK.Xmax() < Kmax) + Kmax = aSigmaK.Xmax(); + if(Kmax <= Kmin) + return 0.; + + double z = aParticle.Time.z(); + int iZ= fStepZ>0 ? ((int)(z/fStepZ)):0; + + double totalRate = 0.;//rate times 8*E*beta + + bool zDependenceExists = (iZ<(int)fIntegrals.size()-1); + SafePtr<RateKKern> kern; + if(zDependenceExists) + kern = new RateKKernZ(*fIntegrals[iZ], *fIntegrals[iZ + 1], iZ * fStepZ, (iZ + 1) * fStepZ, + aSigmaK, aParticle); + else + kern = new RateKKern(*fIntegrals[iZ], aSigmaK, aParticle); + + if(MathUtils::SampleLogDistribution(*kern, aRand, aK, totalRate, Kmin, Kmax, fEpsRel)) + { + double gamma=aParticle.Energy/m; + return totalRate/(2.0*gamma*gamma*aParticle.beta()); + } + aK =0; + return 0; +} + +double ContinuousBackgroundIntegral::GetRateS(const Function &aSigma, const Particle &aParticle, double aAbsError) +{ + double SminSigma = aSigma.Xmin(); + ASSERT_VALID_NO(SminSigma); + double beta = aParticle.beta(); + double m=aParticle.Mass(); + double Smax = m*m+2.*aParticle.Energy*fMaxK*(1.+beta); + double SminKinematic = m*m+2.*aParticle.Energy*fMinK*(1.-beta); + double Smin = SminKinematic<SminSigma ? SminSigma : SminKinematic; + + if(Smax<=Smin) + return 0.; + if(aSigma.Xmax()<Smax) + Smax=aSigma.Xmax(); + + double z = aParticle.Time.z(); + int iZ= fStepZ>0 ? ((int)(z/fStepZ)):0; + int maxIntervals = (int)(log10(Smax/Smin)/log10(fLogStepK)/fEpsRel*1e-2 + 100); + + RateSKern leftKern(*fIntegrals[iZ], aSigma, aParticle); + + double leftRate = math.Integration_qag(leftKern,Smin,Smax,0,fEpsRel,maxIntervals); + ASSERT_VALID_NO(leftRate); + + double result = 0; + if(iZ==(int)fIntegrals.size()-1) + result = leftRate; + else + { + RateSKern rightKern(*fIntegrals[iZ + 1], aSigma, aParticle); + double rightRate = math.Integration_qag(rightKern,Smin,Smax,aAbsError,fEpsRel,maxIntervals); + ASSERT_VALID_NO(rightRate); + result = leftRate + (rightRate-leftRate)/fStepZ*(z-iZ*fStepZ); + ASSERT_VALID_NO(result); + } + return result/8./aParticle.Energy/aParticle.Energy/aParticle.beta(); +} + +double ContinuousBackgroundIntegral::GetRateK(const Function &aSigmaK, const Particle &aParticle, double aAbsError) +{ + double KminSigma = aSigmaK.Xmin(); + ASSERT_VALID_NO(KminSigma); + double beta = aParticle.beta(); + double m=aParticle.Mass(); + double Kmax = aParticle.Energy * fMaxK * (1. + beta)/m; + double KminKinematic = aParticle.Energy * fMinK * (1. - beta)/m; + double Kmin = KminKinematic < KminSigma ? KminSigma : KminKinematic; + + if(Kmax <= Kmin) + return 0.; + if(aSigmaK.Xmax() < Kmax) + Kmax = aSigmaK.Xmax(); + + double z = aParticle.Time.z(); + int iZ= fStepZ>0 ? ((int)(z/fStepZ)):0; + int maxIntervals = (int)(log10(Kmax / Kmin) / log10(fLogStepK) / fEpsRel * 1e-2 + 100); + + RateKKern leftKern(*fIntegrals[iZ], aSigmaK, aParticle); + + double leftRate = math.Integration_qag(leftKern, Kmin, Kmax, 0, fEpsRel, maxIntervals); + ASSERT_VALID_NO(leftRate); + + double result = 0; + if(iZ==(int)fIntegrals.size()-1) + result = leftRate; + else + { + RateKKern rightKern(*fIntegrals[iZ + 1], aSigmaK, aParticle); + double rightRate = math.Integration_qag(rightKern, Kmin, Kmax, aAbsError, fEpsRel, maxIntervals); + ASSERT_VALID_NO(rightRate); + result = leftRate + (rightRate-leftRate)/fStepZ*(z-iZ*fStepZ); + ASSERT_VALID_NO(result); + } + double gamma=aParticle.Energy/m; + return result/(2.0*gamma*gamma*aParticle.beta()); +} + +ContinuousBackgroundIntegral::~ContinuousBackgroundIntegral() +{ + //ASSERT(fData.size()==fIntegrals.size());//fData may be empty if this object was created with Clone() method + for(int iZ=fData.size()-1; iZ>=0; iZ--) + { + delete fData[iZ]; + } + for(int iZ=fIntegrals.size()-1; iZ>=0; iZ--) + { + delete fIntegrals[iZ]; + } + fData.clear(); + fIntegrals.clear(); +} + +void ContinuousBackgroundIntegral::UnitTest() +{ + double zMax = 1; + double kStep = pow(10,0.05); + double cmbTemp = 2.73/Units::phTemperature_mult/units.Eunit; + + ////// Background integral calculation test + + double kMin = 1e-3*cmbTemp; + double kMax = 1e3*cmbTemp; + PlankBackground plank(cmbTemp, kMin, kMax, 0, zMax); + ContinuousBackgroundIntegral bi(plank, 0.1, kStep, zMax, 1e-3); + Function& I = *(bi.fIntegrals[0]); + double kMaxI = PlankBackgroundIntegral(cmbTemp, kMax); + std::cout << "#Z=0" << "\n"; + for(double k=0.5*kMin; k<2*kMax; k*=kStep) + { + std::cout << k/cmbTemp << "\t" << I(k) << "\t" << PlankBackgroundIntegral(cmbTemp, k) - kMaxI << '\n'; + } + std::cout << "\n\n#table z=0\n"; + I.Print(std::cout,1./cmbTemp,1.); + + std::cout << "\n\n#Z=" << zMax << "\n"; + Function& I1 = *(bi.fIntegrals[bi.fIntegrals.size()-1]); + kMaxI = PlankBackgroundIntegral((1.+zMax)*cmbTemp, kMax); + for(double k=0.5*kMin; k<2*kMax; k*=kStep) + { + std::cout << k/cmbTemp << "\t" << I1(k) << "\t" << PlankBackgroundIntegral((1.+zMax)*cmbTemp, k) - kMaxI << '\n'; + } + + ///// Total rate and sampling test z=0 + + class TestSigma : public Function + { + public: + virtual double f(double s) const { return 1./s;} + virtual double Xmin() const {return sMin;} + virtual double Xmax() const {return sMax;} + double sMin; + double sMax; + }; + + kMin = 1e-6*cmbTemp; + kMax = 1e-5*cmbTemp; + PlankBackground plankLow(cmbTemp, kMin, kMax, 0, zMax); + ContinuousBackgroundIntegral biLow(plankLow, 0.1, kStep, zMax, 1e-3); + TestSigma ts; + Particle p(Photon, 0.); + p.Energy = 10; + ts.sMin = 2*kMin*4*p.Energy; + ts.sMax = 0.5*kMax*4*p.Energy; + double rate = biLow.GetRateS(ts, p); + kMaxI = PlankBackgroundIntegral(cmbTemp, kMax); + double thRate = PlankBackgroundTestRate(cmbTemp,p.Energy,ts.sMax) - + PlankBackgroundTestRate(cmbTemp,p.Energy,ts.sMin) - kMaxI*(ts.sMax-ts.sMin)/8./p.Energy/p.Energy; + double S; + biLow.fEnableDebugOutput = true; + double rateSample = biLow.GetRateAndSampleS(ts, p, 0.5, S); + biLow.fEnableDebugOutput = false; + + cout << "#Total rate test (z=0): rate/sample rate/thRate : " << rate << " / " << rateSample << " / " << thRate << "\n"; + cout << "#<rand> <S> <Smin> <Smax>\n"; + for(double rand=0.; rand<=1.; rand+=0.1) + { + biLow.GetRateAndSampleS(ts, p, rand, S); + cout << rand << "\t" << S << "\t" << ts.sMin << "\t" << ts.sMax << "\n"; + } + + ///// Total rate and sampling test z=0.55*zMax + + double z = 0.55*zMax; + p.Time.setZ(z); + cmbTemp *= (1.+z); + rate = biLow.GetRateS(ts, p); + + kMaxI = PlankBackgroundIntegral(cmbTemp, kMax); + thRate = PlankBackgroundTestRate(cmbTemp,p.Energy,ts.sMax) - + PlankBackgroundTestRate(cmbTemp,p.Energy,ts.sMin) - kMaxI*(ts.sMax-ts.sMin)/8./p.Energy/p.Energy; + + biLow.fEnableDebugOutput = true; + rateSample = biLow.GetRateAndSampleS(ts, p, 0.5, S); + biLow.fEnableDebugOutput = false; + + cout << "#Total rate test(z=" << z << ") : rate/sample rate/thRate : " << rate << " / " << rateSample << " / " << thRate << "\n"; + cout << "#<rand> <S> <Smin> <Smax>\n"; + for(double rand=0.; rand<=1.; rand+=0.1) + { + biLow.GetRateAndSampleS(ts, p, rand, S); + cout << rand << "\t" << S << "\t" << ts.sMin << "\t" << ts.sMax << "\n"; + } +} + +double ContinuousBackgroundIntegral::PlankBackgroundTestRate(double aT, double aE, double aS) +{ + double x = 0.25*aS/aE/aT; + return 0.5*aT*aT/aE/M_PI/M_PI*(0.25*x*x + x - x*log(x)); +} + +double ContinuousBackgroundIntegral::PlankBackgroundIntegral(double aT, double aK) +{ + double gamma = aK/aT; + double result = 0; + if(gamma<1e-5) + result = 0.5*gamma-gamma*gamma/24.0+gamma*gamma*gamma*gamma/2880.-log(gamma); + else if(gamma<700) + result = gamma-log(exp(gamma)-1); + else + result = 0; + return aT/M_PI/M_PI*result; +} + +BackgroundIntegral* MonochromaticBackgroundIntegral::Clone() const +{ + return new MonochromaticBackgroundIntegral(*fConcentration.Clone(), *fEnergy.Clone(), fLogStepE, fEpsRel); +} + +MonochromaticBackgroundIntegral::RateSKern::RateSKern(const Function& aSigma, const Particle& aParticle) : + fSigma(aSigma) +{ + fM2 = aParticle.Mass(); + fM2*=fM2; +} +//(s-m^2) sigma(s) I((s-m^2)/2/E/(1+beta),z) +double MonochromaticBackgroundIntegral::RateSKern::f(double s) const +{ + return fSigma(s)*(s-fM2); +} + +double MonochromaticBackgroundIntegral::RateKKern::f(double k) const +{ + return fSigma(k)*k; +} + +double MonochromaticBackgroundIntegral::GetRateAndSampleS(const Function& aSigma, const Particle& aParticle, double aRand, double& aS, double aAbsError) +{ + ASSERT(aRand>0 || aRand<1); + aS=0; + double z = aParticle.Time.z(); + double n=fConcentration.f(z); + if(n<=0.) + return 0.; + double k=fEnergy.f(z); + + double SminSigma = aSigma.Xmin(); + ASSERT_VALID_NO(SminSigma); + double beta = aParticle.beta(); + double m=aParticle.Mass(); + + double Smax = m*m+2.*aParticle.Energy*k*(1.+beta); + double SminKinematic = m*m+2.*aParticle.Energy*k*(1.-beta); + double Smin = SminKinematic<SminSigma ? SminSigma : SminKinematic; + if(aSigma.Xmax()<Smax) + Smax=aSigma.Xmax(); + if(Smax<=Smin) + return 0.; + + double totalRate = 0.;//rate times 8*E*beta + RateSKern kern(aSigma, aParticle); + + if(MathUtils::SampleLogDistribution(kern, aRand, aS, totalRate, Smin, Smax, fEpsRel)) + { + return totalRate*n/k/k/8./aParticle.Energy/aParticle.Energy/aParticle.beta(); + } + aS=0; + return 0; +} + +double MonochromaticBackgroundIntegral::GetRateS(const Function& aSigma, const Particle& aParticle, double aAbsError) +{ + double z = aParticle.Time.z(); + double n=fConcentration.f(z); + if(n<=0.) + return 0.; + double k=fEnergy.f(z); + double SminSigma = aSigma.Xmin(); + ASSERT_VALID_NO(SminSigma); + double beta = aParticle.beta(); + double m=aParticle.Mass(); + double Smax = m*m+2.*aParticle.Energy*k*(1.+beta); + double SminKinematic = m*m+2.*aParticle.Energy*k*(1.-beta); + double Smin = SminKinematic<SminSigma ? SminSigma : SminKinematic; + + if(Smax<=Smin) + return 0.; + if(aSigma.Xmax()<Smax) + Smax=aSigma.Xmax(); + + int maxIntervals = (int)(log10(Smax/Smin)/log10(fLogStepE)/fEpsRel*1e-2 + 100); + + RateSKern kern(aSigma, aParticle); + + double rate = math.Integration_qag(kern,Smin,Smax,aAbsError,fEpsRel,maxIntervals); + ASSERT_VALID_NO(rate); + + return rate*n/k/k/8./aParticle.Energy/aParticle.Energy/aParticle.beta(); +} + +HighRedshiftBackgrExtension::HighRedshiftBackgrExtension(IBackground* aBackground, double aDeltaZconst, double aPowerLow, double aDeltaZexp, double aZmax): + fBackground(aBackground), + fPowerLow(aPowerLow), + fDeltaZexp(aDeltaZexp), + fZmax(aZmax), + fInnerZmax(fBackground->MaxZ()) +{ + if(aDeltaZconst<0) + Exception::Throw("HighRedshiftBackgrExtension: invalid DeltaZconst"); + fZconst=fInnerZmax+aDeltaZconst; +} + +double HighRedshiftBackgrExtension::n(double aE, double aZ) const +{ + if(aZ<=fInnerZmax) + return fBackground->n(aE, aZ); + if(aZ>fZmax) + return 0.; + double expansionFactor = (1.+aZ)/(1+fInnerZmax); + expansionFactor *= (expansionFactor*expansionFactor);// comoving volumes ratio + + if(aZ<fZconst) + return expansionFactor*fBackground->n(aE, 0.9999*fInnerZmax); + + double powerLowFactor = pow((1.+aZ)/(1.+fZconst),fPowerLow); + double expFactor = (fDeltaZexp>0)?exp((fZconst-aZ)/fDeltaZexp):1; + + return expansionFactor*powerLowFactor*expFactor*fBackground->n(aE, 0.9999*fInnerZmax); +} + +double HighRedshiftBackgrExtension::MinE() const +{ + return fBackground->MinE(); +} + +double HighRedshiftBackgrExtension::MaxE() const +{ + return fBackground->MaxE(); +} + +double HighRedshiftBackgrExtension::MinZ() const +{ + return fBackground->MinZ(); +} + +double HighRedshiftBackgrExtension::MaxZ() const +{ + return fZmax; +} + +std::string HighRedshiftBackgrExtension::Name() const +{ + std::string name = "extended "; + name += fBackground->Name(); + return name; +} + +HighRedshiftBackgrExtension::~HighRedshiftBackgrExtension() +{ + delete fBackground; +} + +CuttedBackground::CuttedBackground(IBackground* aBackgr, double aMinE, double aMaxE, double aMinZ, double aMaxZ): + fBackgr(aBackgr),fMinE(aMinE),fMaxE(aMaxE),fMinZ(aMinZ),fMaxZ(aMaxZ) +{ + if(aBackgr->MinZ()>aMinZ) + fMinZ = aBackgr->MinZ(); + if(aBackgr->MinE()>aMinE) + fMinE = aBackgr->MinE(); + if(aBackgr->MaxZ()<aMaxZ) + fMaxZ = aBackgr->MaxZ(); + if(aBackgr->MaxE()<aMaxE) + fMaxE = aBackgr->MaxE(); +} + +double CuttedBackground::n(double aE, double aZ) const { + return (aE<=fMaxE && aE>=fMinE && aZ<=fMaxZ && aZ>=fMinZ) ? fBackgr->n(aE,aZ) : 0.; +} + +double CuttedBackground::MinE() const { + return fMinE; +} + +double CuttedBackground::MaxE() const { + return fMaxE; +} + +double CuttedBackground::MinZ() const { + return fMinZ; +} + +double CuttedBackground::MaxZ() const { + return fMaxZ; +} + +std::string CuttedBackground::Name() const { + return "Cutted " + fBackgr->Name(); +} +double MonochromaticBackgroundIntegral::GetRateAndSampleK(const Function &aSigmaK, const Particle &aParticle, + Randomizer &aRandomizer, double &aK, double aAbsError) { + double aRand = aRandomizer.Rand(); + aK=0; + double z = aParticle.Time.z(); + double n=fConcentration.f(z); + if(n<=0.) + return 0.; + double eps =fEnergy.f(z); + + double KminSigma = aSigmaK.Xmin(); + ASSERT_VALID_NO(KminSigma); + double beta = aParticle.beta(); + double m=aParticle.Mass(); + + double Kmax = aParticle.Energy * eps * (1. + beta)/m; + double KminKinematic = aParticle.Energy * eps * (1. - beta)/m; + double Kmin = KminKinematic < KminSigma ? KminSigma : KminKinematic; + if(aSigmaK.Xmax() < Kmax) + Kmax =aSigmaK.Xmax(); + if(Kmax <= Kmin) + return 0.; + + double totalRate = 0.; + RateKKern kern(aSigmaK); + + if(MathUtils::SampleLogDistribution(kern, aRand, aK, totalRate, Kmin, Kmax, fEpsRel)) + { + double gamma = aParticle.Energy/m; + return 0.5*totalRate * n /(eps * eps * gamma * gamma * aParticle.beta()); + } + aK=0; + return 0; +} + +double MonochromaticBackgroundIntegral::GetRateK(const Function &aSigmaK, const Particle &aParticle, + double aAbsError) { + double z = aParticle.Time.z(); + double n=fConcentration.f(z); + if(n<=0.) + return 0.; + double eps = fEnergy.f(z); + double KminSigma = aSigmaK.Xmin(); + ASSERT_VALID_NO(KminSigma); + double KmaxSigma = aSigmaK.Xmin(); + double beta = aParticle.beta(); + double m=aParticle.Mass(); + double KmaxKinematic = aParticle.Energy * eps * (1. + beta)/m; + double KminKinematic = aParticle.Energy * eps * (1. - beta)/m; + + double Kmin = KminKinematic<KminSigma ? KminSigma : KminKinematic; + double Kmax = KmaxSigma<KmaxKinematic ? KmaxSigma : KmaxKinematic; + + if(Kmax<=Kmin) + return 0.; + + int maxIntervals = (int)(log10(Kmax/Kmin)/log10(fLogStepE)/fEpsRel*1e-2 + 100); + + RateKKern kern(aSigmaK); + + double rate = math.Integration_qag(kern,Kmin,Kmax,aAbsError,fEpsRel,maxIntervals); + ASSERT_VALID_NO(rate); + double gamma = aParticle.Energy/m; + + return 0.5*rate * n /(eps * eps * gamma * gamma * aParticle.beta()); +} +} /* namespace mcray */ diff --git a/src/lib/Background.h b/src/lib/Background.h new file mode 100644 index 0000000..fbb6ab7 --- /dev/null +++ b/src/lib/Background.h @@ -0,0 +1,442 @@ +/* + * Background.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef BACKGROUND_H_ +#define BACKGROUND_H_ + +#include "TableFunction.h" +#include <limits> +#include "Particle.h" +#include "Randomizer.h" + +namespace mcray { + +using namespace Utils; + +class IBackground { +public: + /*! + Calculate density E*dn(E)/dE at given redshift in internal units. Physical density should be returned, not a comoving one + @param[in] aE background photon energy (in internal units) + @param[in] aZ red shift + @return background photon density E*dn(E)/dE in internal units + */ + virtual double n(double aE, double aZ) const = 0; + virtual double MinE() const = 0; + virtual double MaxE() const = 0; + virtual double MinZ() const = 0; + virtual double MaxZ() const = 0; + virtual std::string Name() const = 0; + virtual ~IBackground(){}; +}; + +class BackgroundUtils +{ +public: + static double CalcIntegralDensity(const IBackground& aBackground, double aRelError=1e-3); + static void Print(const IBackground* aBackground, double aZ, std::ostream& aOut, int nIntervals = 100); + static void UnitTest(const IBackground& aBackground, int aPointsZ=5, int aPointsE = 100); +private: + static double DensityCalculator (double x, void * params); +}; + +class TableBackground : public IBackground{ +public: + TableBackground(std::string aName); + + virtual ~TableBackground(); + + double n(double aE, double aZ) const; + double MinE() const; + double MaxE() const; + double MinZ() const; + double MaxZ() const; + std::string Name() const { return fName; } +protected: + /*! + @param[in] aDir directory containing data files + @param[in] aFileList list of file names convertable to double (represent value of z). Last element in the array should be NULL, the files should be ordered increasingly. + @param[in] aIsLogscaleY if true left and right values of functions in fData will be set to -Infinity, otherwise they will be set to 0 + */ + void Init(std::string aDir, const char** aFileList, bool aIsLogscaleY); + //convert energy in internal units to table x scale + virtual double EtoRecordX(double aE, double aZ) const = 0; + //retrieve energy in internal units from the data record + virtual double recordToE(double aX, double aZ) const = 0; + ////retrieve concentration E*dn/dE in internal units from the data record + virtual double recordToN(double aX, double aY, double aZ) const = 0; +private: + std::vector<GSLTableFunc*> fData; + Vector fZ; + Vector fZindex; + GSLTableFunc* fZtoIndex; + double fMinE; + double fMaxE; + std::string fName; +}; + +/// This class was originally designed to read slightly modified Elmag background tables +/// (see script ElmagTest/Tables/makeTablesFormcray.sh) +/// The table file is supposed to use the following format +/// z1 z2 z3 ... zMax +/// E1 dN/dE(z1,E1) dN/dE(z2,E1) dN/dE(z3,E1) ... dN/dE(zMax,E1) +/// E2 dN/dE(z1,E2) dN/dE(z2,E2) dN/dE(z3,E2) ... dN/dE(zMax,E2) +/// ................ +/// Emax dN/dE(z1,Emax) dN/dE(z2,Emax) dN/dE(z3,Emax) ... dN/dE(zMax,Emax) +/// +/// where [E]=eV, [dN/dE] = 1/cm^3/eV +/// Linear interpolation in z, log(E) and log (N) is performed as in Elmag +/// By default for all z<z_min dN/dE(z) == dN/dE(zMin) +class MatrixBackground : public IBackground{ +public: + MatrixBackground(std::string aName, std::string aTableFile, bool aIsComoving, bool aExtendToZero); + + virtual ~MatrixBackground(); + + double n(double aE, double aZ) const; + double MinE() const { return fMinE; } + double MaxE() const { return fMaxE; } + double MinZ() const { return fExtendToZero ? 0 : fZ[0]; } //For all z<z_min dN/dE(z) == dN/dE(zMin) if aExtendToZero == true + double MaxZ() const { return *(fZ.end()-1); } + std::string Name() const { return fName; } +private: + std::vector<Vector*> fLogEdN_dE; + Vector fZ; + Vector fZindex; + GSLTableFunc* fZtoIndex; + Vector fLogE; + Vector fLogEindex; + GSLTableFunc* fLogEtoIndex; + double fMinE; + double fMaxE; + std::string fName; + bool fExtendToZero; + bool fIsComoving; + char* fBuffer; +}; + +class PlankBackground : public IBackground{ +public: + /*! + @param[in] aT0 background temperature (internal units) + @param[in] aEmin0 minimal background energy (internal units) + @param[in] aEmax maximal background energy (internal units) + @param[in] aMinZ minimal background red shift + @param[in] aMaxZ maximal background red shift + */ + PlankBackground(double aT0, double aEmin, double aEmax, double aMinZ=0, double aMaxZ=10000, bool aFixedTemperature = false); + double n(double aE, double aZ) const; + double MinE() const { return fMinE; } + double MaxE() const { return fMaxE; } + double MinZ() const { return fMinZ; } + double MaxZ() const { return fMaxZ; } + std::string Name() const { return fName; } + virtual ~PlankBackground(){}; +private: + double fT0; + double fMinE; + double fMaxE; + double fMinZ; + double fMaxZ; + std::string fName; + bool fFixedTemperature; +}; + +class GaussianBackground : public IBackground{ +public: + /*! + @param[in] aE background photons energy central value (internal units) + @param[in] aSigma Gaussian distribution sigma (internal units) + @param[in] aWidth width at which the distribution is cut (in units of sigma) + @param[in] aConcentration integral concentration of photons (internal units) + @param[in] aMinZ minimal background red shift + @param[in] aMaxZ maximal background red shift + */ + GaussianBackground(double aE, double aSigma, double aWidth, double aConcentration, double aMinZ=0, double aMaxZ=10000); + double n(double aE, double aZ) const; + double MinE() const { return fMinE; } + double MaxE() const { return fMaxE; } + double MinZ() const { return fMinZ; } + double MaxZ() const { return fMaxZ; } + std::string Name() const { return fName; } + virtual ~GaussianBackground(){}; +private: + double fE0; + double fSigma; + double fMinE; + double fMaxE; + double fMinZ; + double fMaxZ; + double fNorm; + double fWidth; + std::string fName; +}; + +class CompoundBackground : public IBackground{ +public: + //argument ownership is transfered to CompoundBackground object + void AddComponent(IBackground* aBackground, double aWeight=1.); + double n(double aE, double aZ) const; + double MinE() const; + double MaxE() const; + double MinZ() const; + double MaxZ() const; + std::string Name() const; + virtual ~CompoundBackground(); +private: + std::vector<IBackground*> fBackgrounds; + std::vector<double> fWeights; +}; + +class BackgroundIntegral : public SmartReferencedObj +{ +public: + ///calculate rates using sigma(s) where s - total energy squared in center of mass frame + virtual double GetRateAndSampleS(const Function& aSigmaS, const Particle& aParticle, Randomizer& aRandomizer, double& aS, double aAbsError=0) = 0;//todo: make use of aAbsError parameter in propagation engine + virtual double GetRateS(const Function &aSigmaS, const Particle &aParticle, double aAbsError = 0) = 0;//todo: make use of aAbsError parameter in propagation engine + + ///calculate rates using sigma(k) where k - background photon energy in the massive particle rest frame + virtual double GetRateAndSampleK(const Function& aSigmaK, const Particle& aParticle, Randomizer& aRandomizer, double& aK, double aAbsError = 0) = 0; + virtual double GetRateK(const Function &aSigmaK, const Particle &aParticle, double aAbsError = 0) = 0; + virtual BackgroundIntegral* Clone() const = 0; +}; + +class MonochromaticBackgroundIntegral : public BackgroundIntegral +{ + class RateSKern : public Function + { + public: + RateSKern(const Function& aSigmaS, const Particle& aParticle); + + //(s-m^2) sigma(s) + double f(double _x) const; + private: + const Function& fSigma; + double fM2; + }; + class RateKKern : public Function + { + public: + RateKKern(const Function& aSigmaK):fSigma(aSigmaK){}; + //k sigma(k) + double f(double _x) const; + private: + const Function& fSigma; + }; +public: + /*! + @param[in] aConcentration Z-dependence of background particles concentration (internal units) + @param[in] aEnergy Z-dependence of background particles energy (internal units) + @param[in] aLogStepE energy binning used to calculate rates and sample S + @param[in] aEpsRel maximal relative error criterion (used in rate calculation) + */ + MonochromaticBackgroundIntegral(Function& aConcentration, Function& aEnergy, double aLogStepE, double aEpsRel): + fConcentration(aConcentration), + fEnergy(aEnergy), + fLogStepE(aLogStepE), + fEpsRel(aEpsRel){}; + MonochromaticBackgroundIntegral(const MonochromaticBackgroundIntegral& aBackgroundIntegral); + + + double GetRateAndSampleS(const Function& aSigmaS, const Particle& aParticle, Randomizer& aRandomizer, double& aS, double aAbsError=0){ + return GetRateAndSampleS(aSigmaS, aParticle, aRandomizer.Rand(), aS, aAbsError); + } + + double GetRateS(const Function& aSigma, const Particle& aParticle, double aAbsError=0); + + double GetRateAndSampleK(const Function& aSigmaK, const Particle& aParticle, Randomizer& aRandomizer, double& aK, double aAbsError = 0); + double GetRateK(const Function &aSigmaK, const Particle &aParticle, double aAbsError = 0); + + BackgroundIntegral* Clone() const; + virtual ~MonochromaticBackgroundIntegral(){}; +private: + double GetRateAndSampleS(const Function& aSigmaS, const Particle& aParticle, double aRand, double& aS, double aAbsError=0); + MathUtils math; + bool fEnableDebugOutput; + Function& fConcentration; + Function& fEnergy; + double fLogStepE; + const double fEpsRel; +}; + +/// calculates background integral function I(k,z)=Integrate[x^-2 * dn(x,z)/dx, {x, k, Infinity}] +/// calculates Interaction rate Omega=1/8E^2/beta * Integrate[(s-m^2) sigma(s) I((s-m^2)/2/E/(1+beta),z), {s,s_min,Infinity}] +/// The class is not thread safe. Copy should be created for each thread using copy constructor; +class ContinuousBackgroundIntegral : public BackgroundIntegral{ + class Kern : public Function + { + public: + Kern(IBackground& aBackground) : fBackground(aBackground){} + //x^-2 * dn(x,z)/dx === x^-3 x dn/dx + virtual double f(double _x) const; + double Z; + private: + const IBackground& fBackground; + }; + class RateSKern : public Function + { + public: + RateSKern(const Function& aBackrgoundIntegral, const Function& aSigma, const Particle& aParticle); + + //(s-m^2) sigma(s) I((s-m^2)/2/E/(1+beta),z) + double f(double _x) const; + private: + const Function& fBackrgoundIntegral; + protected: + const Function& fSigma; + double fM2; + // 1/2/E/(1+beta) + double fMult; + // 1/2/E/(1-beta) or -1 if beta=1 + double fMaxEmult; + }; + class RateSKernZ : public RateSKern + { + public: + RateSKernZ(const Function& aBackrgoundIntegralZ1, const Function& aBackrgoundIntegralZ2, double aZ1, double aZ2, const Function& aSigma, const Particle& aParticle); + + //(s-m^2) sigma(s) I((s-m^2)/2/E/(1+beta),z) + double f(double _x) const; + private: + const Function& fBackrgoundIntegralZ1; + const Function& fBackrgoundIntegralZ2; + + double fCoef1; + double fCoef2; + }; + class RateKKern : public Function + { + public: + RateKKern(const Function& aBackrgoundIntegral, const Function& aSigma, const Particle& aParticle); + double f(double _x) const; + private: + const Function& fBackrgoundIntegral; + protected: + const Function& fSigma; + + // m/E/(1+beta) + double fMult; + // m/E/(1-beta) or -1 if beta=1 + double fMaxEmult; + }; + class RateKKernZ : public RateKKern + { + public: + RateKKernZ(const Function& aBackrgoundIntegralZ1, const Function& aBackrgoundIntegralZ2, double aZ1, double aZ2, const Function& aSigma, const Particle& aParticle); + double f(double _x) const; + private: + const Function& fBackrgoundIntegralZ1; + const Function& fBackrgoundIntegralZ2; + + double fCoef1; + double fCoef2; + }; +public: + ContinuousBackgroundIntegral(IBackground& aBackground, double aStepZ, double aLogStepK, double aZmaxLimit, double aEpsRel = 0); + BackgroundIntegral* Clone() const; + double GetRateAndSampleS(const Function& aSigmaS, const Particle& aParticle, Randomizer& aRandomizer, double& aS, double aAbsError = 0){ + return GetRateAndSampleS(aSigmaS, aParticle, aRandomizer.Rand(), aS, aAbsError); + } + double GetRateS(const Function &aSigmaS, const Particle &aParticle, double aAbsError = 0); + + double GetRateAndSampleK(const Function& aSigmaK, const Particle& aParticle, Randomizer& aRandomizer, double& aK, double aAbsError = 0){ + return GetRateAndSampleK(aSigmaK, aParticle, aRandomizer.Rand(), aK, aAbsError); + } + double GetRateK(const Function &aSigmaK, const Particle &aParticle, double aAbsError = 0); + + virtual ~ContinuousBackgroundIntegral(); + static void UnitTest(); +private: + double GetRateAndSampleS(const Function& aSigmaS, const Particle& aParticle, double aRand, double& aS, double aAbsError = 0); + double GetRateAndSampleK(const Function& aSigmaK, const Particle& aParticle, double aRand, double&aK, double aAbsError = 0); + ContinuousBackgroundIntegral(const ContinuousBackgroundIntegral& aBackgroundIntegral); + //used in unit test + static double PlankBackgroundIntegral(double aT, double aE); + //used in unit test + static double PlankBackgroundTestRate(double aT, double aE, double aS); + + double I(double k, double z); + + double fStepZ; + double fLogStepK; + double fZmaxLimit; + double fMaxK; + double fMinK; + const double fEpsRel; + std::vector<Function*> fIntegrals;//integrals I for different values of z + std::vector<Vector*> fData; + Vector fBinningK; + MathUtils math; + bool fEnableDebugOutput; +}; + +class HighRedshiftBackgrExtension : public IBackground +{ +public: + HighRedshiftBackgrExtension(IBackground* aBackground, double aDeltaZconst, double aPowerLow, double aDeltaZexp, double aZmax=1000); + virtual double n(double aE, double aZ) const; + virtual double MinE() const; + virtual double MaxE() const; + virtual double MinZ() const; + virtual double MaxZ() const; + virtual std::string Name() const; + virtual ~HighRedshiftBackgrExtension(); +private: + IBackground* fBackground; + double fZconst; + double fPowerLow; + double fDeltaZexp; + double fZmax; + double fInnerZmax; +}; + +class CuttedBackground : public IBackground +{ +public: + //takes ownership of aBackgr + CuttedBackground(IBackground* aBackgr, double aMinE=0, double aMaxE=1e300, double aMinZ=0, double aMaxZ=1e300); + virtual double n(double aE, double aZ) const; + virtual double MinE() const; + virtual double MaxE() const; + virtual double MinZ() const; + virtual double MaxZ() const; + virtual std::string Name() const; +private: + SafePtr<IBackground> fBackgr; + double fMinE; + double fMaxE; + double fMinZ; + double fMaxZ; +}; + + +typedef SmartPtr<BackgroundIntegral> PBackgroundIntegral; + + + +} /* namespace mcray */ +#endif /* BACKGROUND_H_ */ diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt new file mode 100644 index 0000000..4262323 --- /dev/null +++ b/src/lib/CMakeLists.txt @@ -0,0 +1,134 @@ +cmake_minimum_required (VERSION 2.6) +project (mcray) +enable_language (Fortran) + +# FFLAGS depend on the compiler +get_filename_component (Fortran_COMPILER_NAME ${CMAKE_Fortran_COMPILER} NAME) + +if (Fortran_COMPILER_NAME MATCHES "gfortran.*") + # gfortran + set (CMAKE_Fortran_FLAGS "-ffixed-line-length-200 -fno-f2c") + set (CMAKE_Fortran_FLAGS_RELEASE "-O2") + set (CMAKE_Fortran_FLAGS_DEBUG "-O0 -g") +elseif (Fortran_COMPILER_NAME MATCHES "ifort.*") + # ifort (untested) + set (CMAKE_Fortran_FLAGS_RELEASE "-f77rtl -O2") + set (CMAKE_Fortran_FLAGS_DEBUG "-f77rtl -O0 -g") +elseif (Fortran_COMPILER_NAME MATCHES "g77") + # g77 + set (CMAKE_Fortran_FLAGS_RELEASE "-fno-f2c -O2") + set (CMAKE_Fortran_FLAGS_DEBUG "-fno-f2c -O0 -g") +else (Fortran_COMPILER_NAME MATCHES "gfortran.*") + message ("CMAKE_Fortran_COMPILER full path: " ${CMAKE_Fortran_COMPILER}) + message ("Fortran compiler: " ${Fortran_COMPILER_NAME}) + message ("No optimized Fortran compiler flags are known, we just try -O2...") + set (CMAKE_Fortran_FLAGS_RELEASE "-O2") + set (CMAKE_Fortran_FLAGS_DEBUG "-O0 -g") +endif (Fortran_COMPILER_NAME MATCHES "gfortran.*") + + +add_definitions(-DUSE_GSL) +include_directories(../OS/include ../external) + +set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG -O0 -fopenmp") +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG -O0 -fopenmp -pg") +set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wall -O2 -fopenmp") +set(CMAKE_PREFIX_PATH "../external") + +# -rdynamic flux is only supported on systems with ELF executable format +IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux") + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -rdynamic") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -rdynamic") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -rdynamic") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -rdynamic") +endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux") + +#FIND_LIBRARY(FORTRAN_LIBRARY gfortran PATHS OS/lib) +#FIND_LIBRARY(OMP_LIBRARY gomp PATHS OS/lib) +FIND_LIBRARY(C_LIBRARY c) +FIND_LIBRARY(XERCES_LIBRARY xerces-c) +FIND_LIBRARY(GSL_LIBRARY gsl) +FIND_LIBRARY(GSLCBLAS_LIBRARY gslcblas) + +set(SOURCE_FILES +../external/SOPHIA/eventgen.f + ../external/SOPHIA/inpoutput.f + ../external/SOPHIA/jetset74dp.f + ../external/SOPHIA/rndm.f + ../external/SOPHIA/sampling.f +sophia2cpp.f +Sophia.cpp +Sophia.h +Background.cpp +Background.h +Cosmology.cpp +Cosmology.h +Debug.cpp +Debug.h +Deflection1D.cpp +Deflection1D.h +Deflection3D.cpp +Deflection3D.h +Filter.cpp +Filter.h +GammaPP.cpp +GammaPP.h +GZK.cpp +GZK.h +ICS.cpp +ICS.h +Inoue12IROSpectrum.cpp +Inoue12IROSpectrum.h +Interaction.cpp +Interaction.h +MathUtils.cpp +MathUtils.h +NeutronDecay.cpp +NeutronDecay.h +Nucleus.cpp +Nucleus.h +Output.cpp +Output.h +PPP.cpp +PPP.h +Particle.cpp +Particle.h +ParticleStack.cpp +ParticleStack.h +PropagationEngine.cpp +PropagationEngine.h +ProtonPP.cpp +ProtonPP.h +Randomizer.cpp +Randomizer.h +TableBackgrounds.cpp +TableBackgrounds.h +TableFunction.cpp +TableFunction.h +TableReader.cpp +TableReader.h +Test.cpp +Test.h +Thinning.cpp +Thinning.h +Units.cpp +Units.h +Utils.cpp +Utils.h +main.cpp +PhotoDisintegration.cpp +PhotoDisintegration.h +SteckerEBL.cpp +SteckerEBL.h +Stecker16Background.cpp +Stecker16Background.h +Logger.cpp +Logger.h) + +add_library(mcray ${SOURCE_FILES}) +add_executable(mcray_test ExampleUserMain.cpp) +add_executable(z2t z2t.cpp) +target_link_libraries(mcray gfortran gomp ${OMP_LIBRARY} ${C_LIBRARY} ${GSL_LIBRARY} ${GSLCBLAS_LIBRARY}) +target_link_libraries(mcray_test mcray) +target_link_libraries(z2t mcray) + diff --git a/src/lib/Cosmology.cpp b/src/lib/Cosmology.cpp new file mode 100644 index 0000000..17fea76 --- /dev/null +++ b/src/lib/Cosmology.cpp @@ -0,0 +1,204 @@ +/* + * Cosmology.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "Cosmology.h" +#include "Units.h" +#include "gsl/gsl_sf_hyperg.h" +#include "gsl/gsl_sf_gamma.h" +#include <iostream> +#include "Particle.h" + +namespace mcray { + +Cosmology cosmology; + +void Cosmology::Init(cosmo_time aMaxZ, cosmo_time aOmegaVac, cosmo_time aHubbleKm_sec_Mpc, int aAccuracy) +{ + ASSERT(t0==0);//init should be called only once + ASSERT(aMaxZ>0); + ASSERT(aOmegaVac>=0 && aOmegaVac<=1.); + ASSERT(aAccuracy>100); + Accuracy = aAccuracy; + + H = 1e5*aHubbleKm_sec_Mpc/Units::Mpc_in_cm*units.Tunit; + fMaxZ = aMaxZ; + + Lv = aOmegaVac; + Lm = 1.-Lv; + sqrtLv = sqrt(Lv); + sqrtLm = sqrt(Lm); + + if(Lv <= 0.)//Lyambda = 0 + { + t0 = 2./3./H; + } + else + { + t0 = log((2. + 2.*sqrtLv - Lm)/Lm)/(3.*sqrtLv)/H; + //t0 = 2./3./H/sqrtLv*log((1+sqrtLv)/sqrtLm); + } + initZ2T(); + initZ2D(); +} + +Cosmology::Cosmology() +{ + Lv = 0.; + Lm = 0.; + t0 = 0.; + sqrtLm = 0.; + sqrtLv = 0.; + sqrtLv = 0.; + Accuracy = 0; +} + +void Cosmology::initZ2D() +{ + ASSERT(fMaxZ>0.);// must be initialized + + z.push_back(0); + d.push_back(0); + + cosmo_time aMin = 1./(fMaxZ+1); + cosmo_time da = (1.-aMin)/Accuracy; + cosmo_time da2 = 0.5*da; + cosmo_time a = 1.-da; + cosmo_time prev = diffD(1.); + cosmo_time sum = 0.; + + for(int i=1; i<=Accuracy; i++, a -= da) + { + sum += prev; + sum += 4.*diffD(a+da2); + prev = diffD(a); + sum += prev; + z.push_back(1./a-1.); + d.push_back(da*sum/6.); + } + z2dFunc = new LinearFuncX<cosmo_time >(z,d,0,d[Accuracy]); + d2zFunc = new LinearFuncX<cosmo_time >(d,z,0,z[Accuracy]); +} + +void Cosmology::print() const +{ + std::cout << "#z\td\n"; + for(int i=0; i<Accuracy; i++) + { + std::cout << z[i] << "\t" << d[i]*units.Lunit/Units::Mpc_in_cm << "\n"; + } + std::cout << "\n\n"; + std::cout << "#z\tt\n"; + for(int j=0; j<Accuracy; j++) + { + std::cout << zt[j] << "\t" << tt[j]*units.Lunit/Units::Mpc_in_cm << "\n"; + } +} + +void Cosmology::UnitTest() +{ + cosmology.Init(10); + cosmo_time z=1; + Particle p(Photon, z); + std::cout << "z=" << z << "\tt=" << p.Time.t()*units.Lunit/Units::Mpc_in_cm << " Mpc d=" + << cosmology.z2d(z)*units.Lunit/Units::Mpc_in_cm << " Mpc\n\n"; + std::cout << "Age of universe " << cosmology.z2t(0)*units.Lunit/Units::Mpc_in_cm << " Mpc\n\n"; + cosmology.print(); +} + +void Cosmology::initZ2T() +{ + ASSERT(fMaxZ);// must be initialized + + zt.push_back(0); + tt.push_back(0); + + cosmo_time dz = fMaxZ/Accuracy; + cosmo_time dz2 = 0.5*dz; + cosmo_time z = dz; + cosmo_time prev = diffT(0.); + cosmo_time sum = 0.; + + for(int i=1; i<=Accuracy; i++, z += dz) + { + sum += prev; + sum += 4.*diffT(z-dz2); + prev = diffT(z); + sum += prev; + zt.push_back(z); + tt.push_back(dz*sum/6.); + } + z2tFunc = new LinearFuncX<cosmo_time>(zt,tt,0,(tt)[Accuracy]); + t2zFunc = new LinearFuncX<cosmo_time>(tt,zt,0,(zt)[Accuracy]); +} + +///The function is used to test red shift evolution in special case of the source with no evolution in comoving frame and alpha = 2 +cosmo_time Cosmology::TestIntegral(cosmo_time r /*r===min(Emax/E, 1+Zmax)*/) const +{ + return TestIntegralF(r)-TestIntegralF(1.); +} + +cosmo_time Cosmology::Hy2F1(cosmo_time a, cosmo_time b, cosmo_time c, cosmo_time z) +{ + if(fabs(z)<=1) + return gsl_sf_hyperg_2F1(a,b,c,z); + z = 1./z; + cosmo_time mult1 = gsl_sf_gamma(b-a)*gsl_sf_gamma(c)/gsl_sf_gamma(b)/gsl_sf_gamma(c-a); + mult1 *= pow(-z, a); + cosmo_time mult2 = gsl_sf_gamma(a-b)*gsl_sf_gamma(c)/gsl_sf_gamma(a)/gsl_sf_gamma(c-b); + mult2 *= pow(-z, b); + cosmo_time f1 = gsl_sf_hyperg_2F1(a,a-c+1,a-b+1,z); + cosmo_time f2 = gsl_sf_hyperg_2F1(b,b-c+1,b-a+1,z); + cosmo_time result = mult1*f1 + mult2*f2; + return result; +} + +///The function is used to test red shift evolution in special case of the source with no evolution in comoving frame and alpha = 2 +cosmo_time Cosmology::TestIntegralF(cosmo_time a/*a===1+z*/) const //F[1+z, alpha] = (1+z)^(-alpha)*(Lm*(1+z)^3 + Lv)^(-1/2) +{//TestIntegralF === Integrate[F[1+z, 1], z] + cosmo_time a3 = a*a*a; + cosmo_time arg = -(a3*Lm)/Lv; + cosmo_time Hypergeometric2F1 = Hy2F1(0.6666666666666666,0.5,1.6666666666666667, + arg); + cosmo_time result = (sqrt(1 + (-1 + a3)*Lm)* + (4*(-1 + Lm)*sqrt((-1 + Lm - a3*Lm)/ + (-1 + Lm)) + + a3*Lm*Hypergeometric2F1))/(4.*a*Lv*Lv* + sqrt((-1 + Lm - a3*Lm)/(-1 + Lm))); + return result; +} + +void Cosmology::SuppressionTest() const +{ + std::cerr << "red shift spectrum suppression test for m-alpha = -2 and Zmax = " << fMaxZ << ":\n"; +//here we simulate how spectrum should be suppressed at highest energies due to red shifting +//The analytic estimate was made for spectrum Q(E,z) = E^-alpha * (1+z)^(3+m) * Theta(Emax-E), for m-alpha = -2 + cosmo_time norm = 1./TestIntegral(1.+fMaxZ); + for(cosmo_time r=1; r<fMaxZ + 1; r+=0.05) + std::cerr << r << "\t" << TestIntegral(r)*norm << "\n"; +} + +} /* namespace mcray */ diff --git a/src/lib/Cosmology.h b/src/lib/Cosmology.h new file mode 100644 index 0000000..0ef3160 --- /dev/null +++ b/src/lib/Cosmology.h @@ -0,0 +1,240 @@ +/* + * Cosmology.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef COSMOLOGY_H_ +#define COSMOLOGY_H_ + +#include "Utils.h" +#include "TableFunction.h" +#include <math.h> + +namespace mcray { + +using namespace Utils; +typedef long double cosmo_time; + +class Cosmology +{ +public: + Cosmology(); + void Init(cosmo_time aMaxZ=100., cosmo_time aOmegaVac=0.692, cosmo_time aHubbleKm_sec_Mpc=67.8, int aAccuracy=100000); +// z -> t, dz -> dt and back transformation + inline cosmo_time z2t0(cosmo_time _z) const;//t0==0 at z=0 + inline cosmo_time t02z(cosmo_time _t) const;//t0==0 at z=0 + inline cosmo_time z2t(cosmo_time _z) const;//t==0 at z=infinity + inline cosmo_time t2z(cosmo_time _t) const;//t==0 at z=infinity + inline cosmo_time z2d(cosmo_time _z) const; + inline cosmo_time d2z(cosmo_time _d) const; + inline cosmo_time getLv() const{return Lv;}; + inline cosmo_time getAgeOfUniverse() const{return t0;}; + void SuppressionTest() const; + //returns energy loss rate -1/E dE/dt due to redshift at + inline cosmo_time eLossRate(cosmo_time aZ) const; + + // -dt/dz + inline cosmo_time diffT(cosmo_time aZ) const; + + //Hubble parameter at given redshift + inline cosmo_time Hubble(cosmo_time aZ) const; + //test output + void print() const; + static void UnitTest(); + inline bool IsInitialized() { return t0!=0; } + inline cosmo_time RelError() const { return 1./Accuracy; } +private: + //test + static cosmo_time Hy2F1(cosmo_time a, cosmo_time b, cosmo_time c, cosmo_time z); + cosmo_time TestIntegralF(cosmo_time r) const; + cosmo_time TestIntegral(cosmo_time r) const; + + inline cosmo_time diffD(cosmo_time a) const; + void initZ2D(); + void initZ2T(); + + cosmo_time H;//Hubble parameter at z=0 (internal units) + cosmo_time Lv;// omega_lambda at z=0 + cosmo_time Lm;// omega_matter at z=0 + cosmo_time sqrtLv;//sqrt(Lv) + cosmo_time sqrtLm;//sqrt(Lv) + cosmo_time t0;//universe age at z=0 + cosmo_time fMaxZ; + int Accuracy; + std::vector<cosmo_time> zt; + std::vector<cosmo_time> tt; + std::vector<cosmo_time> z; + std::vector<cosmo_time> d; + SafePtr< LinearFuncX<cosmo_time> > z2dFunc; + SafePtr< LinearFuncX<cosmo_time> > d2zFunc; + SafePtr< LinearFuncX<cosmo_time> > z2tFunc; + SafePtr< LinearFuncX<cosmo_time> > t2zFunc; +}; + +inline cosmo_time Cosmology::t2z(cosmo_time _t) const +{ + return t02z(_t-t0); +} + +inline cosmo_time Cosmology::z2t0(cosmo_time _z) const//t0==0 at z=0 +{ + ASSERT(z2tFunc->InTableRange(_z)); + return -z2tFunc->f(_z); +} + +inline cosmo_time Cosmology::t02z(cosmo_time _t0) const//t0==0 at z=0 +{ + cosmo_time timeAgoFromNow = -_t0; + if(timeAgoFromNow<0) + { + ASSERT(-timeAgoFromNow/t0<1e-12);//roundoff error + timeAgoFromNow=0.; + } + ASSERT(t2zFunc->InTableRange(timeAgoFromNow)); + return t2zFunc->f(timeAgoFromNow); +} + +inline cosmo_time Cosmology::z2t(cosmo_time _z) const +{ + ASSERT(z2tFunc->InTableRange(_z)); + return t0 - z2tFunc->f(_z); +} + +inline cosmo_time Cosmology::eLossRate(cosmo_time aZ) const +{ + return 1./(1.+aZ)/diffT(aZ); +} + +inline cosmo_time Cosmology::diffD(cosmo_time a) const +{ + return 1./( H*sqrt(a*(Lm+Lv*a*a*a)) ); +} + +inline cosmo_time Cosmology::z2d(cosmo_time _z) const +{ + ASSERT(z2dFunc->InTableRange(_z)); + return z2dFunc->f(_z); +} + +inline cosmo_time Cosmology::d2z(cosmo_time _d) const +{ + ASSERT(d2zFunc->InTableRange(_d)); + return d2zFunc->f(_d); +} + +inline cosmo_time Cosmology::diffT(cosmo_time aZ) const +{ + cosmo_time z1 = 1.+aZ; + return 1./H/z1/sqrt(Lm*z1*z1*z1+Lv); +} + +inline cosmo_time Cosmology::Hubble(cosmo_time aZ) const +{ + return 1./((1.+aZ)*diffT(aZ)); +} + +extern Cosmology cosmology; + +class CosmoTime +{ +public: + //construct with z=0 + CosmoTime():m_z(0.),m_t(0.){} + + //constructor with redshift + CosmoTime(cosmo_time aRedshift) {setZ(aRedshift);} + + //copy constructor + CosmoTime(const CosmoTime& aTime):m_z(aTime.m_z), m_t(aTime.m_t){} + + inline operator cosmo_time() const { return t(); } + inline CosmoTime& operator=(const CosmoTime& aTime) { m_z = aTime.m_z; m_t = aTime.m_t; return *this; } + inline CosmoTime& operator=(cosmo_time aT) { setT(aT); return *this; } + inline CosmoTime& operator += (cosmo_time aDt) + { + cosmo_time curT = t(); + cosmo_time newT = curT+aDt; + if(newT>0){ + ASSERT(newT/aDt<cosmology.RelError()); + newT = 0.;//fix roundoff error + } + setT(newT); return *this; + } + + inline bool operator<(const CosmoTime& aT) const + { + ASSERT(m_z>=0. || m_t<=0); + if(m_z<0 || aT.m_z<0) + return t()<aT.t(); + else + return aT.m_z<m_z; + } + + //redshift value + inline cosmo_time z() const + { + ASSERT(m_z>=0. || m_t<=0); + if(m_z<0) + m_z = cosmology.t02z(m_t);//lazy initialization + return m_z; + } + + //time difference from z=0 (<0 for past moments) + inline cosmo_time t() const + { + ASSERT(m_z>=0. || m_t<=0); + if(m_t>0.) + m_t = cosmology.z2t0(m_z);//lazy initialization + return m_t; + } + + //set redshift value z + inline void setZ(cosmo_time aZ) + { + ASSERT(aZ>=0.); + m_z = aZ; + m_t = 1;//enable lazy initialization + } + + //set redshift using time difference from z=0 + inline void setT(cosmo_time aT) + { + ASSERT(aT<=0.); + m_z = -1;//enable lazy initialization + m_t = aT; + } + + std::string ToString() const { + std::ostringstream logStr; + logStr << "z=" << m_z << ", t=" <<m_t; + return logStr.str(); + } +private: + mutable cosmo_time m_z; + mutable cosmo_time m_t;//time difference between z=0 and this z (always < 0) +}; + +} /* namespace mcray */ +#endif /* COSMOLOGY_H_ */ diff --git a/src/lib/Debug.cpp b/src/lib/Debug.cpp new file mode 100644 index 0000000..b652f32 --- /dev/null +++ b/src/lib/Debug.cpp @@ -0,0 +1,265 @@ +/* + * Debug.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "Debug.h" +#include "Utils.h" +#include <iostream> +#include <gsl/gsl_errno.h> +#include <fstream> +#include <omp.h> + +#include <execinfo.h> +#include <errno.h> +#include <cxxabi.h> +#include <stdio.h> +#include <signal.h> +#include <stdlib.h> + + +namespace Utils { + + static inline void printStackTrace( FILE *out = stderr, unsigned int max_frames = 63 ) + { + fprintf(out, "stack trace:\n"); + + // storage array for stack trace address data + void* addrlist[max_frames+1]; + + // retrieve current stack addresses + unsigned int addrlen = backtrace( addrlist, sizeof( addrlist ) / sizeof( void* )); + + if ( addrlen == 0 ) + { + fprintf( out, " \n" ); + return; + } + + // resolve addresses into strings containing "filename(function+address)", + // Actually it will be ## program address function + offset + // this array must be free()-ed + char** symbollist = backtrace_symbols( addrlist, addrlen ); + + size_t funcnamesize = 1024; + char funcname[1024]; + + // iterate over the returned symbol lines. skip the first, it is the + // address of this function. + for ( unsigned int i = 4; i < addrlen; i++ ) + { + char* begin_name = NULL; + char* begin_offset = NULL; + char* end_offset = NULL; + + // find parentheses and +address offset surrounding the mangled name +#ifdef DARWIN + // OSX style stack trace + for ( char *p = symbollist[i]; *p; ++p ) + { + if (( *p == '_' ) && ( *(p-1) == ' ' )) + begin_name = p-1; + else if ( *p == '+' ) + begin_offset = p-1; + } + + if ( begin_name && begin_offset && ( begin_name < begin_offset )) + { + *begin_name++ = '\0'; + *begin_offset++ = '\0'; + + // mangled name is now in [begin_name, begin_offset) and caller + // offset in [begin_offset, end_offset). now apply + // __cxa_demangle(): + int status; + char* ret = abi::__cxa_demangle( begin_name, &funcname[0], + &funcnamesize, &status ); + if ( status == 0 ) + { + funcname = ret; // use possibly realloc()-ed string + fprintf( out, " %-30s %-40s %s\n", + symbollist[i], funcname, begin_offset ); + } else { + // demangling failed. Output function name as a C function with + // no arguments. + fprintf( out, " %-30s %-38s() %s\n", + symbollist[i], begin_name, begin_offset ); + } + +#else // !DARWIN - but is posix + // not OSX style + // ./module(function+0x15c) [0x8048a6d] + for ( char *p = symbollist[i]; *p; ++p ) + { + if ( *p == '(' ) + begin_name = p; + else if ( *p == '+' ) + begin_offset = p; + else if ( *p == ')' && ( begin_offset || begin_name )) + end_offset = p; + } + + if ( begin_name && end_offset && ( begin_name < end_offset )) + { + *begin_name++ = '\0'; + *end_offset++ = '\0'; + if ( begin_offset ) + *begin_offset++ = '\0'; + + // mangled name is now in [begin_name, begin_offset) and caller + // offset in [begin_offset, end_offset). now apply + // __cxa_demangle(): + + int status = 0; + char* ret = abi::__cxa_demangle( begin_name, funcname, + &funcnamesize, &status ); + char* fname = begin_name; + if ( status == 0 ) + fname = ret; + + if ( begin_offset ) + { + fprintf( out, " %-30s ( %-40s + %-6s) %s\n", + symbollist[i], fname, begin_offset, end_offset ); + } else { + fprintf( out, " %-30s ( %-40s %-6s) %s\n", + symbollist[i], fname, "", end_offset ); + } +#endif // !DARWIN - but is posix + } else { + // couldn't parse the line? print the whole line. + fprintf(out, " %-40s\n", symbollist[i]); + } + } + + free(symbollist); + } + + void abortHandler( int signum ) + { + // associate each signal with a signal name string. + const char* name = NULL; + switch( signum ) + { + case SIGABRT: name = "SIGABRT"; break; + case SIGSEGV: name = "SIGSEGV"; break; + case SIGBUS: name = "SIGBUS"; break; + case SIGILL: name = "SIGILL"; break; + case SIGFPE: name = "SIGFPE"; break; + } + + // Notify the user which signal was caught. We use printf, because this is the + // most basic output function. Once you get a crash, it is possible that more + // complex output systems like streams and the like may be corrupted. So we + // make the most basic call possible to the lowest level, most + // standard print function. + if ( name ) + fprintf( stderr, "Caught signal %d (%s)\n", signum, name ); + else + fprintf( stderr, "Caught signal %d\n", signum ); + + // Dump a stack trace. + // This is the function we will be implementing next. + printStackTrace(); + + // If you caught one of the above signals, it is likely you just + // want to quit your program right now. + exit( signum ); + } + +Debug debug; + +bool Debug::fInitialized = false; + +Debug::Debug(LogLevel aLevel) +{ + fInitialized = false; + fTimestamp = false; + fStartTime = time(0); + fThreadFilter = -1; + fLevel = aLevel; + + if(fInitialized) return; + gsl_set_error_handler (GslFrrorHandler); + + signal( SIGABRT, abortHandler ); + signal( SIGSEGV, abortHandler ); + signal( SIGILL, abortHandler ); + signal( SIGFPE, abortHandler ); + + fInitialized = true; +} + +void Debug::SetOutputFile(std::string aFile) +{ +#pragma omp critical (Debug) + { + if(fOut.is_open()) + fOut.close(); + fOut.open(aFile.c_str(),std::ios::out); + } +} + +void Debug::PrintLine(std::string aMessage, LogLevel aLevel) +{ + int threadId = omp_get_thread_num(); + if(fThreadFilter>=0 && threadId!=fThreadFilter) + return; + +#pragma omp critical (Debug) + { + if(IsLogEnabled(aLevel)) { + if (fTimestamp) { + fOut << (time(NULL) - fStartTime) << "\t"; + } + fOut << threadId << "\t" << aMessage << std::endl; + } + //else + // std::cerr << aMessage << std::endl; + } +} + +void Debug::GslFrrorHandler (const char * reason, + const char * file, + int line, + int gsl_errno) +{ + std::ostringstream message; + message << "GSL error " << gsl_errno << " occurred in " << file << "("<< line << ")\nreason:" << reason << "\n"; + std::string msg = message.str(); + LOG_ERROR(msg); +#ifdef _DEBUG + DebugBreakpoint(); +#endif + Exception::Throw(msg); +} + +void Debug::DebugBreakpoint() +{ +/// add breakpoint here + std::cerr << "debug breakpoint reached\n"; +} + +}//end of namespace Utils diff --git a/src/lib/Debug.h b/src/lib/Debug.h new file mode 100644 index 0000000..bc94647 --- /dev/null +++ b/src/lib/Debug.h @@ -0,0 +1,140 @@ +/* + * Debug.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef DEBUG_H +#define DEBUG_H + +#include <string> +#include <sstream> +#include <fstream> + +namespace Utils { + +template<class T> std::string ToString (const T& aValue) +{ + std::ostringstream logStr; + logStr << aValue; + return logStr.str(); +} + +#define NOT_IMPLEMENTED Exception::Throw(Utils::ToString("not implemented: " __FILE__ "(") + Utils::ToString(__LINE__) + ")"); + + enum LogLevel{ + ErrorLL=0, + WarningLL, + MessageLL, + VerboseLL + }; + +class Debug{ +public: + Debug(LogLevel aLevel = MessageLL); + static void DebugBreakpoint(); + void SetOutputFile(std::string aFile); + void PrintLine(std::string aMessage, LogLevel aLogLevel = MessageLL); + void EnableTimestamp(bool aEnable = true){ + fTimestamp = aEnable; + } + //set to negative to disable thread filtering + void SetThreadFilter(int aFilter=-1){ + fThreadFilter = aFilter; + } + + void SetLevel(LogLevel aLevel){ + fLevel = aLevel; + } + + static inline int IsInfinity(double _val) /* returns nonzero value if argument is Infinity */ + { + if(_val>1.6e308) + return 1; + if(_val<(-1.6e308)) + return -1; + return 0; + } + static int IsValid(double _val) /* returns zero if _val<0 or is Infinity */ + { + if(_val>=0 && _val < 1.6e308) + { + return 1; + } + return 0; + } + inline bool IsLogEnabled(LogLevel aLevel) { return aLevel<=fLevel && fOut.is_open(); } +private: + static void GslFrrorHandler (const char * reason, + const char * file, + int line, + int gsl_errno); + static bool fInitialized; + bool fTimestamp; + time_t fStartTime; + int fThreadFilter; + LogLevel fLevel; + std::ofstream fOut; +}; + +extern Debug debug; + +}//namespace Utils { + +//macro to ensure log output is calculated only if log is enabled +#define LOG_OUTPUT(str,level) if(Utils::debug.IsLogEnabled(level)) { Utils::debug.PrintLine((str), level);}; + +#define LOG_VERBOSE(str) LOG_OUTPUT(str,VerboseLL) +#define LOG_MESSAGE(str) LOG_OUTPUT(str,MessageLL) +#define LOG_WARNING(str) LOG_OUTPUT(str,WarningLL) +#define LOG_ERROR(str) LOG_OUTPUT(str,ErrorLL) + +/////////////////////////////////////////////////////////////////////////// +// Preprocessor definitions for diagnostic support +/////////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG + +#define ASSERT(f) {if(!(f))\ +{\ + Utils::Debug::DebugBreakpoint();\ + Utils::Exception::Throw(Utils::ToString(__FILE__ "(") + Utils::ToString(__LINE__) + ") assertion failed: " #f );\ +};} +#define VERIFY(f) ASSERT(f) +#define DEBUG_ONLY(f) (f) +#define ASSERT_VALID_NO(f) ASSERT(Utils::Debug::IsValid(f)) +#define VERIFY_VALID_NO(f) ASSERT(Utils::Debug::IsValid(f)) + +#else // _DEBUG + +#define ASSERT(f) ((void)0) +#define VERIFY(f) ((void)(f)) +#define DEBUG_ONLY(f) ((void)0) +#define ASSERT_VALID_NO(f) ((void)0) +#define VERIFY_VALID_NO(f) ((void)(f)) + +#endif // _DEBUG + + +#endif /* DEBUG_H */ + diff --git a/src/lib/Deflection1D.cpp b/src/lib/Deflection1D.cpp new file mode 100644 index 0000000..e7ebc51 --- /dev/null +++ b/src/lib/Deflection1D.cpp @@ -0,0 +1,127 @@ +/* + * Deflection1D.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "Deflection1D.h" + +namespace Interactions { + +Deflection1D::Deflection1D(Function* aB, Function* aLcor): + fB(aB),fLcor(aLcor) +{ +} + +Deflection1D::~Deflection1D() { + +} + +bool Deflection1D::Propagate(cosmo_time aDeltaT, Particle& aParticle, Randomizer& aRandomizer) const +{ + double Lcor = fLcor->f(aParticle.Time.z()); + + if(aParticle.ElectricCharge()!=0) + { + if(aParticle.CorrelatedBpath>=Lcor) + { + aParticle.Deflection2 += (aParticle.CorrelatedDeflection*aParticle.CorrelatedDeflection); + aParticle.LastB = -1.; + } + if(aParticle.LastB < 0.) + { + aParticle.CorrelatedBpath = 0; + aParticle.CorrelatedDeflection = 0; + } + cosmo_time corDeflDt = Lcor - aParticle.CorrelatedBpath; + + for(cosmo_time timeLeft = aDeltaT; timeLeft>0;) + { + cosmo_time rate = Rate(aParticle); + if(corDeflDt>timeLeft) + { + aParticle.CorrelatedBpath += timeLeft; + aParticle.CorrelatedDeflection += rate*timeLeft;//Theta in radians + break; + } + else + { + aParticle.CorrelatedBpath = 0.; + double corDefl = aParticle.CorrelatedDeflection + rate*corDeflDt;//Theta in radians + aParticle.Deflection2 += corDefl*corDefl; + aParticle.CorrelatedDeflection = 0.; + timeLeft -= corDeflDt; + corDeflDt = Lcor; + aParticle.LastB = -1.; + } + } + aParticle.PropagateFreely(aDeltaT);//we don't modify Pdir in this class + return true; + } + else + { + aParticle.CorrelatedBpath += aDeltaT; + return false; + } +} + +//deflection rate in radians per unit length (internal units) assuming constant field +double Deflection1D::Rate(const Particle& aParticle) const +{ + int q = abs(aParticle.ElectricCharge()); + if(q) + { + if(aParticle.LastB<0.) + aParticle.LastB = fabs(fB->f(aParticle.Time.z())); + double deflectionRate = ((double)q)*0.52*M_PI/180./(aParticle.Energy/units.TeV)/(10.*units.kpc)*aParticle.LastB*1e15;//formula (14) arxiv/1106.5508v2 or (9) from astro-ph/9604098 + ASSERT_VALID_NO(deflectionRate); + return deflectionRate; + } + return 0.; +} + +Function* Deflection1D::RandomDirectionB::Clone() const +{ + return new RandomDirectionB(fAbsBvalue, fRandomizer.CreateIndependent()); +} + + +double Deflection1D::RandomDirectionB::f(double aZ) const +{ + double cos = fRandomizer.Rand()*2.0 - 1; + double sin = sqrt(1-cos*cos); + return fAbsBvalue*sin; +} + +double Deflection1D::ConstComovingCorrelationLength::f(double aZ) const +{ + return fCorL/(1.+aZ); +} + +Function* Deflection1D::ConstComovingCorrelationLength::Clone() const +{ + return new RandomDirectionB(fCorL); +} + +} /* namespace mcray */ diff --git a/src/lib/Deflection1D.h b/src/lib/Deflection1D.h new file mode 100644 index 0000000..84dcadd --- /dev/null +++ b/src/lib/Deflection1D.h @@ -0,0 +1,97 @@ +/* + * Deflection1D.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef DEFLECTION1D_H_ +#define DEFLECTION1D_H_ + +#include "Interaction.h" +#include "MathUtils.h" + +namespace Interactions { +using namespace mcray; +using namespace Utils; + +//Small deflection approximation (assuming spherical symmetry) +class Deflection1D : public DeflectionInteraction{ + class RandomDirectionB : public Function + { + public: + RandomDirectionB(double aAbsBvalue, unsigned long int aSeed=0) : fRandomizer(aSeed), fAbsBvalue(fabs(aAbsBvalue)){} + double f(double aZ) const;//sign of B is disregarded since sum of squares is used + virtual Function* Clone() const; + private: + mutable Randomizer fRandomizer; + double fAbsBvalue; + }; + class ConstComovingCorrelationLength : public Function + { + public: + ConstComovingCorrelationLength(double aCorL) : fCorL(aCorL){} + double f(double aZ) const; + virtual Function* Clone() const; + private: + double fCorL;; + }; +public: + //Constructor with dependences of magnetic field abs value and correlation length on distance to the source in terms of redshift + Deflection1D(Function* aB, Function* aLcor); + + Deflection1D(double aBgauss, double aLcorMpc, unsigned long int aRandomSeed) + { + fLcor = new ConstComovingCorrelationLength(aLcorMpc*units.Mpc); + fB = new RandomDirectionB(aBgauss, aRandomSeed); + } + virtual ~Deflection1D(); + + virtual bool Propagate(cosmo_time deltaT, Particle &aParticle, Randomizer &aRandomizer) const; + virtual double Rate(const Particle& aParticle) const; + virtual DeflectionInteraction* Clone() const + { + return new Deflection1D(fB->Clone(), fLcor->Clone()); + } +//protected: + //Used for test purposes only (constant B projection value is not physical) + //This method can be used for comparison with kinetic equation based code + //with deflection taken into account in the form of effective electron decay + Deflection1D(double aBgauss, double aLcorMpc) + { + fLcor = new ConstComovingCorrelationLength(aLcorMpc*units.Mpc); + fB = new ConstFunction(aBgauss); + } +private: + // fB->f(z) returns strength of (transverse) extragalactic B-field [Gauss] at redshift z at the distance from the observer + // corresponding to light travel time from redshift z to 0 + // (spherical symmetry is assumed) + SafePtr<Function> fB;//sign of B is disregarded since sum of squares is used + // fLcor->f(z) returns B correlation length at redshift z at the distance from the + // observer corresponding to light travel time from redshift z to 0 + // (spherical symmetry is assumed) + SafePtr<Function> fLcor;//sign of B is disregarded since sum of squares is used +}; + +} /* namespace mcray */ +#endif /* DEFLECTION1D_H_ */ diff --git a/src/lib/Deflection3D.cpp b/src/lib/Deflection3D.cpp new file mode 100644 index 0000000..ef30f02 --- /dev/null +++ b/src/lib/Deflection3D.cpp @@ -0,0 +1,325 @@ +/* + * Deflection3D.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "Deflection3D.h" +#include "Test.h" + + +namespace Interactions { + using namespace mcray; + using namespace Utils; + + + Deflection3D::Deflection3D(MagneticField * aMagnField, u_int aAccuracy) : + fGlobalB(aMagnField), + fAccuracy(aAccuracy) + { + } + + Deflection3D::Deflection3D(uint aAccuracy, int aUniqueMFslot): + fAccuracy(aAccuracy), + fMFslot(aUniqueMFslot) + { + if(fMFslot<0) + fMFslot = Particle::ReserveInteractionDataSlot(); + if(fMFslot<0) + Exception::Throw("Deflection3D: Failed to reserve interaction slot"); + } + + void Deflection3D::GetRotationRate(const Particle &aParticle, const std::vector<double> &aBgauss, std::vector<double>& outRate) + { + //dn/dt = qeB/E, where q is charge in units of electron charge; e=sqrt(alpha)-electron charge + double mult = aParticle.ElectricCharge()*units.Gauss*units.e/aParticle.Energy; + const double* N = aParticle.Pdir; + ASSERT(outRate.size()==3); + outRate[0] = (N[1]* aBgauss[2]- aBgauss[1]*N[2])*mult; + outRate[1] = (N[2]* aBgauss[0]- aBgauss[2]*N[0])*mult; + outRate[2] = (N[0]* aBgauss[1]- aBgauss[0]*N[1])*mult; + } + + double Deflection3D::Rate(const Particle &aParticle) const { + if(!aParticle.ElectricCharge()) + return 0; + std::vector<double> deflRate(3); + std::vector<double> curB(3); + MagneticField* B = fGlobalB; + if(!B) + B = (MagneticField*)(aParticle.interactionData[fMFslot]); + ASSERT(B); + B->GetValueGauss(aParticle.X, aParticle.Time, curB); + GetRotationRate(aParticle, curB, deflRate); + double absRate = sqrt(deflRate[0]*deflRate[0]+deflRate[1]*deflRate[1]+deflRate[2]*deflRate[2]); + return absRate; + } + + bool Deflection3D::Propagate(cosmo_time deltaT, Particle &aParticle, Randomizer &aRandomizer) const { + if(!aParticle.ElectricCharge()) + return false; + cosmo_time initialT = aParticle.Time.t(); + cosmo_time beta = aParticle.beta();//particle speed + MagneticField* pB = fGlobalB; + if(!pB) + pB = (MagneticField*)(aParticle.interactionData[fMFslot]); + ASSERT(pB); + cosmo_time dx = pB->MinVariabilityScale(aParticle.Time)/fAccuracy; + u_int nSteps = (u_int)(deltaT/dx + 1.); + dx = deltaT/nSteps; + std::vector<double> deflRate(3); + std::vector<double> curB(3); + double newPdir[3]; + int totSteps=0;//debug info + + for(u_int stepB = 0; stepB<nSteps; stepB++){ + pB->GetValueGauss(aParticle.X, aParticle.Time, curB); + GetRotationRate(aParticle, curB, deflRate); + double absRate = sqrt(deflRate[0]*deflRate[0]+deflRate[1]*deflRate[1]+deflRate[2]*deflRate[2]); + cosmo_time maxStep = 0.05*M_PI/absRate/fAccuracy;//max step corresponds to 9/fAccuracy degrees turn + u_int nSubSteps = (u_int)(dx/maxStep+1.); + cosmo_time dXsubStep = dx/nSubSteps; + for(u_int substep=0; substep<nSubSteps; substep++){ + if(substep) + GetRotationRate(aParticle, curB, deflRate); + double absPdir = 0.; + for(int i=0; i<3; i++){ + double val = aParticle.Pdir[i] + deflRate[i]*dXsubStep; + newPdir[i] = val; + absPdir += val*val; + } + absPdir = sqrt(absPdir);//norm of new vector + for(int i=0; i<3; i++){ + double val = newPdir[i]/absPdir;//make sure rotated vector has unit norm + aParticle.X[i] += 0.5*(aParticle.Pdir[i] + val)*beta*dXsubStep; + aParticle.Pdir[i] = val; + } + aParticle.Time += dXsubStep; + totSteps++; + } + } + //double timeError = fabs((aParticle.Time.t()-initialT-deltaT)/deltaT)/totSteps; + //double eps = deltaT/initialT; + //char a[1024]; + //sprintf(a, "eps=%e,timeError=%e", eps, timeError); + //ASSERT(timeError<1e-14); # adding small dt to large t leads to calculation error. We minimize the error by using long double for time + aParticle.Time.setT(initialT + deltaT); + //aParticle.Time.setT(finalT);//increase accuracy + return true; + } + + DeflectionInteraction *Deflection3D::Clone() const { + return fGlobalB ? (new Deflection3D(fGlobalB, fAccuracy)) : (new Deflection3D(fAccuracy, fMFslot)); + } + + MonochromaticMF::MonochromaticMF(Randomizer &aRandomizer, double aLamdbaMpc, + double aAmplitudeGauss): + fLambda(aLamdbaMpc*units.Mpc), + fK(2.*M_PI/fLambda), + fAbsBgauss(aAmplitudeGauss), + fBeta(aRandomizer.Rand()*M_PI*2.), + fAlpha(aRandomizer.Rand()*M_PI*2.), + fAmplitude(3) + { + /// Choosing random direction: cos(Theta) and phi are distributed uniformly + fCosTheta = 1.-(aRandomizer.Rand()*2); + fSinTheta = sqrt(1.- fCosTheta * fCosTheta); + double phiK(aRandomizer.Rand()*M_PI*2.); + fCosPhi = cos(phiK); + fSinPhi = sin(phiK); + init(); + } + + MonochromaticMF::MonochromaticMF(double aLamdbaMpc, double aAmplitudeGauss, double aBetaK, + double aAlphaK, double aTheta, double aPhi): + fLambda(aLamdbaMpc*units.Mpc), + fK(2.*M_PI/fLambda), + fAbsBgauss(aAmplitudeGauss), + fBeta(aBetaK), + fAlpha(aAlphaK), + fAmplitude(3), + fCosTheta(cos(aTheta)), + fSinTheta(sin(aTheta)), + fCosPhi(cos(aPhi)), + fSinPhi(sin(aPhi)) + { + init(); + } + + void MonochromaticMF::init() + { + double cosAlpha = cos(fAlpha); + double sinAlpha = sin(fAlpha); + + fAmplitude[0] = std::complex<double>(cosAlpha* fCosTheta * fCosPhi, -sinAlpha* fSinPhi)* fAbsBgauss; + fAmplitude[1] = std::complex<double>(cosAlpha* fCosTheta * fSinPhi, sinAlpha* fCosPhi)* fAbsBgauss; + fAmplitude[2] = std::complex<double>(-cosAlpha* fSinTheta, 0)* fAbsBgauss; + } + + void MonochromaticMF::GetValueGauss(const double *x, const CosmoTime &aTime, + std::vector<double> &outValue) const + { + ASSERT(outValue.size()==3); + double zPrime = fSinTheta * fCosPhi *x[0] + fSinTheta * fSinPhi *x[1] + fCosTheta *x[2]; + std::complex<double> phase(0,fK*zPrime+ fBeta); + std::complex<double> mult = exp(phase); + for(int i=0; i<3; i++){ + outValue[i] = (fAmplitude[i]*mult).real(); + } + } + + double MonochromaticMF::MinVariabilityScale(const CosmoTime &aTime) const { + return fLambda; + } + + int MagneticField::Print(double lambdaMpc, std::ostream& aOutput) { + std::vector<double> B(3); + CosmoTime t(0); + double step=lambdaMpc*units.Mpc/20.; + double L=lambdaMpc*units.Mpc*3; + for(double x=0.; x<=L; x+=step) { + for (double y = 0.; y <= L; y+=step) { + for (double z = 0.; z <= L; z += step) { + const double r[] = {x, y, z}; + GetValueGauss(r, t, B); + aOutput << x / units.Mpc << "\t" << y / units.Mpc << "\t" << z / units.Mpc << "\t" + << B[0] << "\t" << B[1] << "\t" << B[2] << "\n"; + } + aOutput << "\n"; + } + aOutput << "\n\n"; + } + aOutput << "# use gnuplot command \"set pm3d at b; splot 'B' i 0 u 2:3:col\" where col=4,5 or 6 to view the data\n"; + return 0; + } + + int MonochromaticMF::UnitTest() { + std::vector<double> B(3); + CosmoTime t(0); + Randomizer r; + double lambdaMpc = 1.; + double Bgauss = 10.; + MonochromaticMF mf(r,lambdaMpc,Bgauss); + mf.Print(lambdaMpc); + } + + int Deflection3D::UnitTest() { + if(!cosmology.IsInitialized()) + cosmology.Init(); + CosmoTime startTime(0.01); + Particle e(Electron, startTime.z()); + e.Energy = 1e13*units.eV; + Randomizer r; + double lambdaMpc = 1; + double Bgauss = 1e-15; + double dT = units.Mpc*1.; + + double Beta = 0.1; + double Alpha = M_PI/4; + double Theta = 0; + double Phi = 0; + SmartPtr<MagneticField> mf = new MonochromaticMF(lambdaMpc, Bgauss, Beta, Alpha, Theta, Phi); + std::vector<double> B0(3); + mf->GetValueGauss(e.X, e.Time, B0); + double Bperp = sqrt(B0[0]*B0[0]+B0[1]*B0[1]); + Deflection3D defl(mf); + //Deflection3D defl(new MonochromaticMF(r,lambdaMpc,Bgauss)); + defl.Propagate(dT, e, r); + double L = sqrt(e.X[0]*e.X[0]+e.X[1]*e.X[1]+e.X[2]*e.X[2]); + double P = sqrt(e.Pdir[0]*e.Pdir[0]+e.Pdir[1]*e.Pdir[1]+e.Pdir[2]*e.Pdir[2]); + double theta = acos(e.Pdir[2])/M_PI*180.; + + std::cout << e.X[0]/units.Mpc << "\t" << e.X[1]/units.Mpc << "\t" << e.X[2]/units.Mpc << "\n"; + std::cout << "B(0)=" << Bperp << "\t(dt-dL)/dt = " << (dT-L)/dT << "\t|Pdir|=" << P << "\ttheta=" << theta << " deg.\n"; + return 0; + } + + void TurbulentMF::GetValueGauss(const double *x, const CosmoTime &aTime, std::vector<double> &outValue) const { + std::vector<double> wave(3, 0.); + outValue.assign(3, 0.); + for(std::vector<SafePtr<MonochromaticMF> >::const_iterator pw = fWaves.begin(); pw!=fWaves.end(); pw++){ + (*pw)->GetValueGauss(x, aTime, wave); + for(uint i=0; i<3; i++) + outValue[i] += (wave[i]); + } + } + + double TurbulentMF::MinVariabilityScale(const CosmoTime &aTime) const { + return fLc*0.1; + } + + TurbulentMF::TurbulentMF(Randomizer &aRandomizer, double aLcor_Mpc, double aVariance_Gauss, double aMultK): + fLc(aLcor_Mpc*units.Mpc) + { + ASSERT(aMultK>1.); + const double gamma = 11./3.; + std::vector<double> norms; + std::vector<double> Ks; + double sumNorm = 0; + for(double k = 1./fLc/16.; k*fLc < 4e4; k*=aMultK){//here we limit the range of waves by condition normK>~1e-3 + Ks.push_back(k); + double normK = k*k*k/(1.+pow(k*fLc,gamma)); + norms.push_back(normK); + sumNorm += normK; + fWaves.push_back(0); + } + for(uint i=0; i<norms.size(); i++){ + fWaves[i] = new MonochromaticMF(aRandomizer, 2.*M_PI/Ks[i]/units.Mpc, aVariance_Gauss*sqrt(norms[i]/sumNorm)); + } + } + + int TurbulentMF::UnitTest() { + if(!cosmology.IsInitialized()) + cosmology.Init(); + CosmoTime startTime(0.03); + Particle e(Electron, startTime.z()); + e.Energy = 1e13*units.eV; + Randomizer r; + double lambdaMpc = 1; + double Bgauss = 1e-15; + SmartPtr<MagneticField> mf = new TurbulentMF(r, lambdaMpc, Bgauss); + Deflection3D defl(mf); + std::ofstream outB("TurbulentMF_B.txt"); + mf->Print(lambdaMpc, outB); + std::vector<double> B0(3); + double t=0; + std::cout << "E/eV = " << e.Energy/units.eV << "\tB/G = " << Bgauss << "\tLc/Mpc = " << lambdaMpc << "\n"; + mf->GetValueGauss(e.X, e.Time, B0); + double Bperp = sqrt(B0[0]*B0[0]+B0[1]*B0[1]); + std::cout << "t/Mpc=0\tB_xy/G=" << Bperp << "\tr = (" << e.X[0]/units.Mpc << "\t" << e.X[1]/units.Mpc << "\t" << e.X[2]/units.Mpc << ")\n"; + + for(double tMpc=1.; tMpc<100; tMpc*=3) { + defl.Propagate(units.Mpc * (tMpc - t), e, r); + mf->GetValueGauss(e.X, e.Time, B0); + double Bperp = sqrt(B0[0]*B0[0]+B0[1]*B0[1]); + std::cout << "t/Mpc=" << tMpc << "\tB_xy/G=" << Bperp << "\tr = (" << e.X[0]/units.Mpc << "\t" << e.X[1]/units.Mpc << "\t" << e.X[2]/units.Mpc << ")\n"; + double L = sqrt(e.X[0]*e.X[0]+e.X[1]*e.X[1]+e.X[2]*e.X[2]); + double P = sqrt(e.Pdir[0]*e.Pdir[0]+e.Pdir[1]*e.Pdir[1]+e.Pdir[2]*e.Pdir[2]); + double theta = acos(e.Pdir[2])/M_PI*180.; + std::cout << "(dt-dL)/dt = " << (tMpc-L/units.Mpc)/tMpc << "\t|Pdir| = " << P << "\ttheta = " << theta << " deg.\n"; + t=tMpc; + } + + return 0; + } +} \ No newline at end of file diff --git a/src/lib/Deflection3D.h b/src/lib/Deflection3D.h new file mode 100644 index 0000000..97feefc --- /dev/null +++ b/src/lib/Deflection3D.h @@ -0,0 +1,119 @@ +/* + * Deflection3D.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MCRAY_DEFLECTION3D_H +#define MCRAY_DEFLECTION3D_H + +#include <complex> +#include "Interaction.h" +#include "MathUtils.h" + + +namespace Interactions { + using namespace mcray; + using namespace Utils; + + class MagneticField : public SmartReferencedObj{ + public: + virtual void GetValueGauss(const double *x, const CosmoTime &aTime, std::vector<double>& outValue) const = 0; + + virtual double MinVariabilityScale(const CosmoTime &aTime) const = 0; + + int Print(double lambdaMpc, std::ostream& aOutput=std::cout); + }; + + class Deflection3D : public DeflectionInteraction { + public: + //aMagnField should provide magnetic field in Gauss + Deflection3D(MagneticField * aMagnFieldGauss, uint aAccuracy=10); + + //use custom magnetic field from each particle + //@param aUniqueMFslot custom data slot to use or -1 to reserve one + Deflection3D(uint aAccuracy = 10, int aUniqueMFslot = -1); + + virtual double Rate(const Particle &aParticle) const; + + virtual bool Propagate(cosmo_time deltaT, Particle &aParticle, Randomizer &aRandomizer) const; + + virtual DeflectionInteraction *Clone() const; + + //return 3D vector dn/dt === 1/beta dV/dt + static void GetRotationRate(const Particle &aParticle, const std::vector<double> &aBgauss, std::vector<double>& outRate); + static int UnitTest(); + int MFSlot() const { return fMFslot; } + private: + SmartPtr<MagneticField> fGlobalB; + u_int fAccuracy; + int fMFslot; + }; + + //Randomly oriented plane wave with given k + //Generated as fixed k term in formula (3) of J. Giacalone, J.R. Jokipii, 1999, Ap.J., 520:204-214 + class MonochromaticMF : public MagneticField{ + public: + MonochromaticMF(Randomizer &aRandomizer, double aLamdbaMpc, double aAmplitudeGauss); + //constructor with specific orientation and phase + MonochromaticMF(double aLamdbaMpc, double aAmplitudeGauss, double aBetaK, + double aAlphaK, double aTheta, double aPhi); + + virtual void GetValueGauss(const double *x, const CosmoTime &aTime, + std::vector<double> &outValue) const; + virtual double MinVariabilityScale(const CosmoTime &aTime) const; + + static int UnitTest(); + private: + void init(); + private: + double fLambda; + double fK;//2pi/lambda + double fAbsBgauss; + double fBeta; + double fAlpha; + double fCosTheta; + double fSinTheta; + double fCosPhi; + double fSinPhi; + + std::vector<std::complex<double> >fAmplitude; + }; + + //Turbulent magnetic field defined as sum of randomly oriented plane waves + //as in J. Giacalone, J.R. Jokipii, 1999, Ap.J., 520:204-214 formulas (3)-(7) + class TurbulentMF : public MagneticField{ + + public: + TurbulentMF(Randomizer &aRandomizer, double aLcor_Mpc, double aVariance_Gauss, double aMultK=2./* steps in K */); + virtual void GetValueGauss(const double *x, const CosmoTime &aTime, std::vector<double> &outValue) const; + virtual double MinVariabilityScale(const CosmoTime &aTime) const; + static int UnitTest(); + private: + double fLc; + std::vector<SafePtr<MonochromaticMF> > fWaves; + }; + +} +#endif //MCRAY_DEFLECTION3D_H diff --git a/src/lib/ElmagTest.cpp b/src/lib/ElmagTest.cpp new file mode 100644 index 0000000..e8c9678 --- /dev/null +++ b/src/lib/ElmagTest.cpp @@ -0,0 +1,350 @@ +/* + * ElmagTest.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "ElmagTest.h" +#include "Units.h" +#include "Utils.h" +#include "ParticleStack.h" +#include "PropagationEngine.h" +#include "Thinning.h" +#include "TableBackgrounds.h" +#include "mpfrc++/mpreal.h" +#include "Deflection1D.h" + +using namespace mcray; +using namespace Utils; +using namespace Backgrounds; +using namespace Interactions; + +#ifdef NO_FORTRAN +void elmag_init_test_() +{ + throw "Elmag integration is disabled"; +} + +#define rate_bb_(e0,zz,icq) ASSERT(FALSE) +#define sample_photon_(e0,zz,sgam,ierr) ASSERT(FALSE) +#define zpair_(sgam)_ ASSERT(FALSE) +#define sample_electron_(e0,zz,sgam,ierr) ASSERT(FALSE) +#define zics_(e0,sgam) ASSERT(FALSE) +#define zloss_(e0,zz) ASSERT(FALSE) + +#else + +extern "C" void elmag_init_test_(); + +extern "C" double rate_bb_(double* e0,double* zz,int* icq); + +extern "C" void sample_photon_(double* e0,double* zz,double* sgam,int* ierr); // sample c.m. energy for PP interaction + +extern "C" double zpair_(double* sgam); // sample secondary e+ or e- energy fraction for PP interaction + +extern "C" void sample_electron_(double* e0,double* zz,double* sgam,int* ierr); // sample c.m. energy for ICS interaction + +extern "C" double zics_(double* e0,double* sgam); // sample secondary electron(positron) energy fraction for ICS interaction + +extern "C" double zloss_(double* e0,double* zz); // relative energy loss (per cm) (due to under-threshold ICS photons) + +//extern "C" void get_zics_consts_(double* e0,double* sgam,double* mm,double* emin,double* zmin,double* zmax); + +#endif + +namespace Test { + +double ElmagProxy::Rate(const Particle& aParticle) +{ + if(!fEnabled) + Exception::Throw("ElmagTest is not initialized"); + double e0 = aParticle.Energy*1e6*units.Eunit; + double zz = aParticle.Time.z(); + int icq = ParticleTypeToInt(aParticle.Type); + double rate_cm = rate_bb_(&e0,&zz,&icq); + return rate_cm*units.Lunit; +} + +double ElmagProxy::RateICScel(const Particle& aParticle) +{ + if(aParticle.Type != Electron && aParticle.Type != Positron) + return 0.; + if(!fEnabled) + Exception::Throw("ElmagTest is not initialized"); + + double e0 = aParticle.Energy*1e6*units.Eunit; + double zz = aParticle.Time.z(); + double rate_cm = zloss_(&e0,&zz); + return rate_cm*units.Lunit; +} + +int ElmagProxy::ParticleTypeToInt(ParticleType aType) +{ + switch(aType) + { + case Electron: return -1; + case Positron: return 1; + case Photon: return 0; + default: + Exception::Throw("ElmagTest:ParticleTypeToInt unexpected argument"); + return -100; + } +} + +bool ElmagProxy::SampleS(const Particle& aParticle, double& aS) +{ + if(!fEnabled) + Exception::Throw("ElmagTest is not initialized"); + double e0 = aParticle.Energy*1e6*units.Eunit; + double zz = aParticle.Time.z(); + double sgam = 0.; + int ierr = 0.; + if(aParticle.Type == Photon) + { + sample_photon_(&e0,&zz,&sgam,&ierr); + if(ierr) + { + std::cerr << "Elmag::sample_photon exited with error code " << ierr; + return false; + } + } + else if(aParticle.Type == Positron || aParticle.Type == Electron) + { + sample_electron_(&e0,&zz,&sgam,&ierr); + if(ierr) + { + std::cerr << "Elmag::sample_electron exited with error code " << ierr; + return false; + } + } + aS = sgam*1e-12/units.Eunit/units.Eunit; + Debug::PrintLine(ToString(aS)); + return true; +} + +bool ElmagProxy::GetSecondaries(const Particle& aParticle, std::vector<Particle>& aSecondaries, double aS) +{ + if(!fEnabled) + Exception::Throw("ElmagTest is not initialized"); + double e0 = aParticle.Energy*1e6*units.Eunit; + double sgam = aS/(1e-12/units.Eunit/units.Eunit); + if(aParticle.Type == Photon) + { + double r = zpair_(&sgam); + Particle electron = aParticle; + electron.Type = Electron; + electron.Energy = aParticle.Energy*r; + aSecondaries.push_back(electron); + Particle positron = aParticle; + positron.Type = Positron; + positron.Energy = aParticle.Energy*(1.-r); + aSecondaries.push_back(positron); + } + else if(aParticle.Type == Positron || aParticle.Type == Electron) + { + /* + { + const double Esec_min = 1.;//MeV + double yMax = 1.-Esec_min/aParticle.Energy; + double yMin = aParticle.Mass()*aParticle.Mass()/aS; + ASSERT(yMin<=1); + ASSERT(yMax>=yMin); + } + { + double mm,emin,zmin,zmax; + get_zics_consts_(&e0,&sgam,&mm,&emin,&zmin,&zmax); + if(zmin>zmax) + { + mpfr::mpreal Esec_min = "1000000";//MeV + mpfr::mpreal E = e0; + mpfr::mpreal one = "1"; + mpfr::mpreal m = aParticle.Mass()*1000000; + mpfr::mpreal s = sgam; + + mpfr::mpreal yMax = one-Esec_min/E; + mpfr::mpreal yMin = m*m/s; + ASSERT(yMin<=one); + ASSERT(yMax>=yMin); + } + } + */ + double r = zics_(&e0,&sgam); + Particle lepton = aParticle; + lepton.Energy = aParticle.Energy*r; + aSecondaries.push_back(lepton); + Particle photon = aParticle; + photon.Type = Photon; + photon.Energy = aParticle.Energy*(1.-r); + aSecondaries.push_back(photon); + } + return true; +} + +bool ElmagProxy::GetSecondaries(const Particle& aParticle, std::vector<Particle>& aSecondaries) +{ + double s=0; + if(!SampleS(aParticle, s)) + return false; + return GetSecondaries(aParticle, aSecondaries, s); +} + +void ElmagProxy::Enable() +{ + if(!fEnabled) + { + elmag_init_test_(); + fEnabled = true; + } +} + +bool ElmagProxy::fEnabled = false; + +int ElmagTests::Main(int argc, char** argv) +{ + if(argc==0) + { + RatesTest(); + return 0; + } + const char* command = argv[0]; + if(!strcmp(command,"Kachelries")) + { + int modePP = ElmagCalculateRate|ElmagSampleS|ElmagSampleSecondaryEnergy; + int modeICS = ElmagCalculateRate|ElmagSampleS|ElmagSampleSecondaryEnergy|ElmagCalculateCelRate; + if(argc>1) + sscanf(argv[1], "%d", &modePP); + if(argc>2) + sscanf(argv[2], "%d", &modeICS); + KachelriesTest(0.02, modePP, modeICS); + //KachelriesTest(0.15); + } + return 0; +} + +std::string ElmagTests::HumanReadableMode(int aMode) +{ + std::string result = "elmag"; + if(aMode == ElmagDoNotUse) + result += "None"; + if(aMode & ElmagCalculateRate) + result += "Rate"; + if(aMode & ElmagSampleS) + result += "S"; + if(aMode & ElmagSampleSecondaryEnergy) + result += "E"; + if(aMode & ElmagCalculateCelRate) + result += "Cel"; + return result; +} + +void ElmagTests::KachelriesTest(double aZmax, int aElmagUsageModePP, int aElmagUsageModeICS) +{ + if(!cosmology.IsInitialized()) + cosmology.init(0.73, 71, 10); + double logStepK = pow(10,0.05); + double Emin = 1./units.Eunit; + double alphaThinning = 0.5;//alpha = 1 conserves number of particles on the average; alpha = 0 disables thinning + double Bgauss = 1e-17;//Gauss + double LcorMpc = 1.0;//Mpc + ParticleStack particles; + Result result(Emin); + + result.AddOutput(new SpectrumOutput( + "Elmag_Kachelries_Zmax" + ToString(aZmax) + "_PP" + HumanReadableMode(aElmagUsageModePP) + "_ICS" + HumanReadableMode(aElmagUsageModeICS), + Emin, pow(10,0.05))); + + CompoundBackground backgr; + + double cmbTemp = 2.73/Units::phTemperature_mult/units.Eunit; + backgr.AddComponent(new PlankBackground(cmbTemp, 1e-3*cmbTemp, 1e3*cmbTemp)); + backgr.AddComponent(new Kneiske0309EBL()); + + double stepZ = aZmax<0.2 ? aZmax/2 : 0.1; + double epsRel = 1e-3; + + PBackgroundIntegral backgrI = new ContinuousBackgroundIntegral(backgr, stepZ, logStepK, aZmax, epsRel); + + PropagationEngine pe(particles, result, 2011); + EnergyBasedThinning thinning(alphaThinning); + pe.SetThinning(&thinning); + pe.AddInteraction(new RedShift()); + + if(aElmagUsageModeICS&ElmagCalculateCelRate) + pe.AddInteraction(new ElmagIcsCEL()); + else + pe.AddInteraction(new Interactions::IcsCEL(backgrI,Emin)); + + pe.AddInteraction(new ElmagPP(aElmagUsageModePP, new Interactions::GammaPP(backgrI))); + pe.AddInteraction(new ElmagIcs(aElmagUsageModeICS, new Interactions::ICS(backgrI,Emin))); + pe.AddInteraction(new Deflection1D(Bgauss, LcorMpc)); + + int nParticles = 1000; + for(int i=0; i<nParticles; i++) + { + Particle gamma(Photon, aZmax); + gamma.Energy = 1e8/units.Eunit;//MeV + particles.Add(gamma); + } + + pe.Run(); + //pe.RunMultithread(); +} + +void ElmagTests::RatesTest() +{ + if(!cosmology.IsInitialized()) + cosmology.init(0.73, 71, 10); + + double logStepK = pow(10,0.05); + + RedShift rs; + ElmagIcs ics; + ElmagIcsCEL icsCel; + ElmagPP pp; + double Emin=1; + double Emax=1e10; + + std::ofstream ratesOut; + ratesOut.open("elmag_rates",std::ios::out); + + ratesOut << "#E\tRedshiftRate\tIcsRate\tIcsCelRate\tPpRate\t[E]=1eV [Rate]=1/Mpc\n"; + Particle particle(Electron, 0.); + + for(double E=Emin; E<Emax; E*=logStepK) + { + particle.Energy = E/units.Eunit;//MeV + particle.Type = Electron; + double celRate = icsCel.Rate(particle); + double icsRate = ics.Rate(particle); + double redshiftRate = rs.Rate(particle); + particle.Type = Photon; + double ppRate = pp.Rate(particle); + + double mult = 1./units.Mpc; + ratesOut << E*1e6 << "\t" << redshiftRate*mult << "\t" << icsRate*mult << + "\t" << celRate*mult << "\t" << ppRate*mult << std::endl; + } +} + +}//namespace Test { diff --git a/src/lib/ElmagTest.h b/src/lib/ElmagTest.h new file mode 100644 index 0000000..f9b5e13 --- /dev/null +++ b/src/lib/ElmagTest.h @@ -0,0 +1,162 @@ +/* + * ElmagTest.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef ELMAGTEST_H_ +#define ELMAGTEST_H_ + +#include "Particle.h" +#include "Interaction.h" +#include "ICS.h" +#include "GammaPP.h" + +namespace Test { +using namespace mcray; + +/*! + * Propagation parameters such as background model, minimal photon energy etc. are defined in Elmag code, + * see user_sp102.f90 module user_variables) + */ +class ElmagProxy { +public: + static double Rate(const Particle& aParticle); + static double RateICScel(const Particle& aParticle); + static bool GetSecondaries(const Particle& aParticle, std::vector<Particle>& aSecondaries); + static bool SampleS(const Particle& aParticle, double& aS); + static bool GetSecondaries(const Particle& aParticle, std::vector<Particle>& aSecondaries, double aS); + static void Enable(); + static inline bool Enabled() { return fEnabled; } +private: + static int ParticleTypeToInt(ParticleType aType); + static bool fEnabled; +}; + +enum ElmagUsageMode +{ + ElmagDoNotUse = 0, + ElmagCalculateRate = 1, + ElmagSampleS = 2, + ElmagSampleSecondaryEnergy = 4, + ElmagCalculateCelRate = 8 +}; + +class ElmagIcsPP : public RandomInteraction{ +protected: + ElmagIcsPP(): + fExternal(0),fElmagUsageMode(ElmagCalculateRate|ElmagSampleS|ElmagSampleSecondaryEnergy){} +public: + //combination of ElmagUsageMode flags + ElmagIcsPP(int aElmagUsageMode, RandomInteractionS* pExternalInteraction): + fExternal(pExternalInteraction),fElmagUsageMode(aElmagUsageMode) + { + if(fElmagUsageMode!=ElmagDoNotUse) + ElmagProxy::Enable(); + } + virtual bool Filter(ParticleType aType) const = 0; + double Rate(const Particle& aParticle) const + { + if(!Filter(aParticle.Type)) + return 0.; + if(ElmagCalculateRate) + return ElmagProxy::Rate(aParticle); + else + return fExternal->Rate(aParticle); + } + bool GetSecondaries(const Particle& aParticle, std::vector<Particle>& aSecondaries, Randomizer& aRandomizer) const + { + bool result = false; + if(!Filter(aParticle.Type)) + return result; + double s = 0.; + result = (fElmagUsageMode&ElmagSampleS)?ElmagProxy::SampleS(aParticle, s) : fExternal->SampleS(aParticle, s, aRandomizer); + if(!result) + return false; + if(fElmagUsageMode&ElmagSampleSecondaryEnergy) + return ElmagProxy::GetSecondaries(aParticle, aSecondaries,s); + fExternal->SampleSecondaries(aParticle, aSecondaries, s, aRandomizer); + return true; + } +protected: + SmartPtr<RandomInteractionS> fExternal; + int fElmagUsageMode; +}; + +class ElmagIcs : public ElmagIcsPP{ +public: + ElmagIcs(){} + ElmagIcs(int aElmagUsageMode, Interactions::ICS* pExternalInteraction): + ElmagIcsPP(aElmagUsageMode, pExternalInteraction){}; + + RandomInteraction* Clone() const { return new ElmagIcs(fElmagUsageMode, (Interactions::ICS*)fExternal->Clone()); } + bool Filter(ParticleType aType) const + { + return aType == Electron || aType == Positron; + } +}; + +class ElmagPP : public ElmagIcsPP{ +public: + ElmagPP(){} + ElmagPP(int aElmagUsageMode, Interactions::GammaPP* pExternalInteraction): + ElmagIcsPP(aElmagUsageMode, pExternalInteraction){}; + RandomInteraction* Clone() const { return new ElmagPP(fElmagUsageMode,(Interactions::GammaPP*)fExternal->Clone()); } + virtual bool Filter(ParticleType aType) const + { + return aType == Photon; + } +}; + +/*! + * Continuous energy loss rate due to Inverse Compton Scattering (ICS) with soft secondary photons + * (energy lower than minimal value defined in Elmag code see user_sp102.f90 module user_variables) is calculated in this class + */ +class ElmagIcsCEL : public CELInteraction +{ +public: + ElmagIcsCEL() + { + ElmagProxy::Enable(); + } + double Rate(const Particle& aParticle) const + { + return ElmagProxy::RateICScel(aParticle); + } + CELInteraction* Clone() const { return new ElmagIcsCEL();} +}; + +class ElmagTests +{ +public: + static int Main(int argc, char** argv); +private: + static std::string HumanReadableMode(int aMode); + static void KachelriesTest(double aZmax, int aElmagUsageModePP, int aElmagUsageModeICS); + static void RatesTest(); +}; + +} //namespace Test + +#endif /* ELMAGTEST_H_ */ diff --git a/src/lib/ExampleUserMain.cpp b/src/lib/ExampleUserMain.cpp new file mode 100644 index 0000000..fbc702c --- /dev/null +++ b/src/lib/ExampleUserMain.cpp @@ -0,0 +1,113 @@ +/* + * ExampleUserMain.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <cstdlib> +#include "ParticleStack.h" +#include "Utils.h" +#include "Cosmology.h" +#include "PropagationEngine.h" +#include <iostream> +#include "Output.h" +#include "TableBackgrounds.h" +#include "ICS.h" +#include "GammaPP.h" +#include "Test.h" +#include "PrecisionTests.h" + +using namespace std; +using namespace mcray; +using namespace Utils; +using namespace Backgrounds; +using namespace Interactions; + +void BuildInitialState(ParticleStack& particles, double aZmax) +{ + int nParticles = 10000; + for(int i=0; i<nParticles; i++) + { + Particle gamma(Photon, aZmax); + gamma.Energy = 1e7/units.Eunit;//MeV + gamma.Weight = 1; + particles.AddPrimary(gamma); + } +} + +/* + * This is an example of user_main function performing user defined tasks + * the function should be provided by end user + */ +int user_main(int argc, char** argv) { + double Zmax = 0.01; + double Emin = 1./units.Eunit; + double alphaThinning = 1;//alpha = 1 conserves number of particles on the average; alpha = 0 disables thinning + ParticleStack particles; + Result result(Emin); + result.AddOutput(new SpectrumOutput("out", Emin, pow(10, 0.05))); + + cosmology.Init(); + + //CompoundBackground backgr; + //double cmbTemp = 2.73/Units::phTemperature_mult/units.Eunit; + //backgr.AddComponent(new PlankBackground(cmbTemp, 1e-3*cmbTemp, 1e3*cmbTemp)); + //backgr.AddComponent(new Backgrounds::Kneiske1001EBL()); + + //backgr.AddComponent(new Backgrounds::GaussianBackground(1e-6/units.Eunit, 1e-7/units.Eunit, 1., 0.1*units.Vunit)); + //double n = BackgroundUtils::CalcIntegralDensity(backgr)/units.Vunit; + //backgr.AddComponent(new Backgrounds::GaussianBackground(6.3e-10/units.Eunit, 1e-11/units.Eunit, 1., 413*units.Vunit)); + //n = BackgroundUtils::CalcIntegralDensity(backgr)/units.Vunit; + + GaussianBackground b1(1e-6/units.Eunit, 1e-7/units.Eunit, 1., 0.1*units.Vunit); + GaussianBackground b2(6.3e-10/units.Eunit, 1e-11/units.Eunit, 1., 413*units.Vunit); + + + double stepZ = Zmax<0.2 ? Zmax/2 : 0.1; + double logStepK = pow(10,0.05); + double epsRel = 1e-3; + + //BackgroundIntegral backgrI(backgr, stepZ, logStepK, Zmax, epsRel); + PBackgroundIntegral backgrI1 = new ContinuousBackgroundIntegral(b1, stepZ, logStepK, Zmax, epsRel); + PBackgroundIntegral backgrI2 = new ContinuousBackgroundIntegral(b1, stepZ, logStepK, Zmax, epsRel); + + + PropagationEngine pe(particles, result, 2011); + EnergyBasedThinning thinning(alphaThinning); + pe.SetThinning(&thinning); + pe.AddInteraction(new RedShift()); + pe.AddInteraction(new Interactions::ICS(backgrI1,Emin)); + pe.AddInteraction(new Interactions::IcsCEL(backgrI1,Emin)); + pe.AddInteraction(new Interactions::GammaPP(backgrI1)); + pe.AddInteraction(new Interactions::ICS(backgrI2,Emin)); + pe.AddInteraction(new Interactions::IcsCEL(backgrI2,Emin)); + pe.AddInteraction(new Interactions::GammaPP(backgrI2)); + + /// build initial state + BuildInitialState(particles, Zmax); + + pe.Run(); + + return 0; +} diff --git a/src/lib/Filter.cpp b/src/lib/Filter.cpp new file mode 100644 index 0000000..391e794 --- /dev/null +++ b/src/lib/Filter.cpp @@ -0,0 +1,104 @@ +/* + * Filter.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "Filter.h" + +namespace mcray +{ + + JetOutputFilter::JetOutputFilter(double aEmin, double aJetOpenningAngle, double aObservationAngle) : + fEmin(aEmin), + fJetOpenningAngle(aJetOpenningAngle), + fObservationAngle(aObservationAngle) + { + } + + bool JetOutputFilter::Pass(const Particle& aParticle) const + { + if(aParticle.Energy<=fEmin) + return false; + if(fObservationAngle<M_PI_2 && aParticle.ObservationAngle() > fObservationAngle) + return false; + if(fJetOpenningAngle<M_PI && aParticle.JetOpenningAngle() > fJetOpenningAngle) + return false; + return true; + } + + EnergyBasedFilter::EnergyBasedFilter(double aEmin, double aMaxDeflectionAngle) : + fEmin(aEmin), + fMaxDeflectionAngle(aMaxDeflectionAngle) + { + } + + bool EnergyBasedFilter::Pass(const Particle& aParticle) const + { + if(aParticle.Energy<=fEmin) + return false; + if(fMaxDeflectionAngle<M_PI && aParticle.DeflectionAngle() > fMaxDeflectionAngle) + return false; + return true; + } + + JetFilter::JetFilter(double aEmin, double aJetOpenningAngle, double aObservationAngle) : + fEmin(aEmin), + fJetOpenningAngleSin(aJetOpenningAngle<M_PI_2 ? sin(aJetOpenningAngle) : 1.), + fObservationAngleCos(cos(aObservationAngle)), + fObservationAngleSin(sin(aObservationAngle)) + { + } + + bool JetFilter::Pass(const Particle& aParticle) const + { + if(aParticle.Energy<=fEmin) + return false; + if(aParticle.DeflectionAngle()>0.5*M_PI && fJetOpenningAngleSin<1.) + return false;//exclude particles deflected by more than Pi/2 + double sinBeta = sin(aParticle.DeflectionAngle()); + double d = cosmology.z2d(aParticle.SourceParticle->Time.z()); + double l = cosmology.z2d(aParticle.LastDeflectionTime().z()); + + if(sinBeta > d/l*fJetOpenningAngleSin) + return false;//particle can not reach observer from any angle + + double r = sqrt(d*d+l*l-2.*d*l*fObservationAngleCos);//distance(at z=0) from the point of last deflection time to the source + //assuming that particle arrives at the edge of PSF + if(sinBeta > d/r*fObservationAngleSin) + return false;//particle will reach observer from outside PSF + + return true; + } + + ParticleTypeFilter::ParticleTypeFilter(const ParticleType aParticlesToDiscard[]) { + for(int i=0; i<ParticleTypeEOF; i++){ + fFilter[i] = true; + } + if(aParticlesToDiscard) + for(int i=0; aParticlesToDiscard[i]!=ParticleTypeEOF; i++) { + fFilter[aParticlesToDiscard[i]] = false; + } + } +} diff --git a/src/lib/Filter.h b/src/lib/Filter.h new file mode 100644 index 0000000..c011931 --- /dev/null +++ b/src/lib/Filter.h @@ -0,0 +1,98 @@ +/* + * Filter.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef FILTER_H_ +#define FILTER_H_ +#include <math.h> +#include "Particle.h" +#include "Utils.h" + +namespace mcray { + +class IFilter : public Utils::ISmartReferencedObj { +public: + //must be thread-safe + virtual bool Pass(const Particle& aParticles) const = 0; +}; + +class EnergyBasedFilter : public Utils::TSmartReferencedObj<IFilter> +{ +public: + EnergyBasedFilter(double aEmin, double aMaxDeflectionAngle=M_PI); + bool Pass(const Particle& aParticle) const; + virtual ~EnergyBasedFilter(){} +private: + double fEmin; + double fMaxDeflectionAngle; +}; + +class ParticleTypeFilter : public Utils::TSmartReferencedObj<IFilter> +{ +public: +//aParticlesToRemove is array ending with ParticleTypeEOF + ParticleTypeFilter(const ParticleType aParticlesToDiscard[] = 0); + void DiscardParticle(ParticleType aType) {fFilter[aType] = false;} + bool Pass(const Particle& aParticle) const { return fFilter[aParticle.Type]; } + virtual ~ParticleTypeFilter(){} +private: + bool fFilter[ParticleTypeEOF]; +}; + +class JetOutputFilter : public Utils::TSmartReferencedObj<IFilter> +{ +public: + JetOutputFilter(double aEmin, double aJetOpenningAngle=M_PI, double aObservationAngle=M_PI_2); + bool Pass(const Particle& aParticle) const; + virtual ~JetOutputFilter(){} +private: + double fEmin; + double fJetOpenningAngle; + double fObservationAngle; +}; + +class JetRuntimeFilter : public EnergyBasedFilter +{ +public: + JetRuntimeFilter(double aEmin, double aJetOpenningAngle=M_PI, double aObservationAngle=M_PI_2): + EnergyBasedFilter(aEmin, aJetOpenningAngle + aObservationAngle){} +}; + +class JetFilter : public Utils::TSmartReferencedObj<IFilter> +{ +public: + JetFilter(double aEmin, double aJetOpenningAngle=M_PI, double aObservationAngle=M_PI_2); + bool Pass(const Particle& aParticle) const; + virtual ~JetFilter(){} +private: + double fEmin; + double fJetOpenningAngleSin; + double fObservationAngleCos; + double fObservationAngleSin; +}; + +} /* namespace mcray */ +#endif /* FILTER_H_ */ diff --git a/src/lib/GZK.cpp b/src/lib/GZK.cpp new file mode 100644 index 0000000..04a115c --- /dev/null +++ b/src/lib/GZK.cpp @@ -0,0 +1,189 @@ +/* + * GZK.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "GZK.h" +#include "TableFunction.h" +#include "Sophia.h" + +//used for unit test only +#include "PropagationEngine.h" +#include "Inoue12IROSpectrum.h" +#include "TableBackgrounds.h" +#include "NeutronDecay.h" +#include "Test.h" + +using namespace mcray; +namespace Interactions{ + +GZK::GZK(BackgroundIntegral* aBackground, int aRandSeed):fBackground(aBackground) +{ + fMpSophia = SOPHIA::Mass(Proton); + fMnSophia = SOPHIA::Mass(Neutron); + fSigmaN = InitSigma(Neutron); + fSigmaP = InitSigma(Proton); + if(aRandSeed) + SOPHIA::SetRandomSeed(aRandSeed); +} + +Function* GZK::InitSigma(ParticleType aPrim) +{ + std::string tablesDir = "tables/sophia2/"; + Utils::TableReader reader(tablesDir+(aPrim==Proton?"p":"n"), 2); + std::vector<double>& s = reader.getColumn(0); + std::vector<double>& sigma = reader.getColumn(1); + double M = aPrim==Proton?fMpSophia:fMnSophia; + for(int i=0; i<s.size(); i++) + { + double val = M*(M+2.*s[i]*units.GeV); + s[i] = M*(M+2.*s[i]*units.GeV); + sigma[i] *= (units.barn*1e-6); + } + double rightVal = sigma[sigma.size()-1]; + return new LinearFunc(s, sigma, new LogScale(), new LogScale(), 0, rightVal); +} + +double GZK::Rate(const mcray::Particle &aParticle) const { + if(aParticle.Type!=Proton && aParticle.Type!=Neutron) + return 0.; + const Function* sigma = (aParticle.Type==Proton) ? fSigmaP : fSigmaN; + return fBackground->GetRateS(*sigma, aParticle); +} + +RandomInteraction *GZK::Clone() const { + return new GZK(fBackground->Clone()); +} + +bool GZK::SampleS(const mcray::Particle &aParticle, double &aS, + mcray::Randomizer &aRandomizer) const { + if(aParticle.Type!=Proton && aParticle.Type!=Neutron) + return false; + const Function* sigma = aParticle.Type==Proton?fSigmaP:fSigmaN; + if(fBackground->GetRateAndSampleS(*sigma, aParticle, aRandomizer, aS)!=0) + { + const double sophiaThreshold=1.1646*units.GeV*units.GeV; + if(aS<sophiaThreshold) + { + ASSERT(aS>=sophiaThreshold*0.999); + aS = 1.001*sophiaThreshold; + } + return true; + } + else + return false; +} + +void GZK::SampleSecondaries(mcray::Particle &aParticle, + std::vector<mcray::Particle> &aSecondaries, double aS, + mcray::Randomizer &aRandomizer) const { + double M = aParticle.Type==Proton?fMpSophia:fMnSophia; + double epsPrimeGeV = 0.5*(aS/M-M)/units.GeV; + int noSecondaries = 0; + double secEfrac[250]; + int secTypes[250]; +#pragma omp critical (SOPHIA) + {//SOPHIA class is not thread-safe + //todo: separately collect protons and neutrons in two queues and try to handle the queues one after another with critical section disabled + SOPHIA::SamplePhotopionRel(aParticle.Type, epsPrimeGeV, noSecondaries, secEfrac, secTypes); + } + while(--noSecondaries>=0) + { + Particle sec = aParticle; + sec.Type = (ParticleType)secTypes[noSecondaries]; + sec.Energy *= secEfrac[noSecondaries]; + sec.fCascadeProductionTime = aParticle.Time; + aSecondaries.push_back(sec); + } +} + + void GZK::UnitTest() + { + double Zmax = 1; + double Emin = 1e9*units.eV; + double Emax = 1e21*units.eV; + int Nparticles = 10000; + std::string outputDir = "testGZK"; + unsigned int kAc = 1; + double epsRel = 1e-3; + double stepZ = Zmax<0.05 ? Zmax/2 : 0.025; + double logStepK = pow(10,0.05/kAc); + if(!cosmology.IsInitialized()) + cosmology.Init(Zmax + 10); + double cmbTemp = 2.73*units.K; + double alphaThinning = 0.5; + CompoundBackground backgr; + backgr.AddComponent(new PlankBackground(cmbTemp, 1e-3*cmbTemp, 1e3*cmbTemp, 0., Zmax + 1.)); + //IR/O component + backgr.AddComponent(new Backgrounds::Inoue12BaselineIROSpectrum()); //new GaussianBackground(0.1*units.eV, 0.01*units.eV, 5, 1./units.cm3, 0, Zmax + 1.)); + PBackgroundIntegral backgrI(new ContinuousBackgroundIntegral(backgr, stepZ, logStepK, Zmax, epsRel)); + int seed = 2015; + Result result(new EnergyBasedFilter(Emin, M_PI), true); + + SmartPtr<RawOutput> pOutput = new RawOutput(outputDir, false); + result.AddOutput(pOutput); + ParticleStack particles; + PropagationEngine pe(particles, result, seed); + EnergyBasedThinning thinning(alphaThinning); + pe.SetThinning(&thinning); + + Particle proton(Proton, Zmax); + int nSteps = log(Emax/Emin)/log(logStepK)+1.; + double mult = pow(Emax/Emin, 1./nSteps); + { + std::ofstream rateOut; + rateOut.open((outputDir + "/GZK").c_str(),std::ios::out); + PRandomInteraction i = new GZK(backgrI); + pe.AddInteraction(i); + int step=0; + for(proton.Energy=Emax/100; step<=nSteps; proton.Energy*=mult, step++) + rateOut << proton.Energy/units.eV << "\t" << i->Rate(proton)*units.Mpc << "\n"; + rateOut.close(); + } + Randomizer rand; + CosmoTime tEnd; + proton.Energy = Emax; + pOutput->SetOutputDir(outputDir + "/z0"); + result.SetEndTime(tEnd); + + for(int i=0; i<Nparticles; i++) + { + particles.AddPrimary(proton); + } + pe.RunMultithreadReleaseOnly(); + } + + void GZK::UnitTestEconserv(double aE, double aZ){ + int nParticles = 100; + Test::EConservationTest test(aZ,1e6,2015); + test.alphaThinning = 0.0;//alpha = 1 conserves number of particles on the average; alpha = 0 disables thinning + test.Engine().AddInteraction(new GZK(test.Backgr(),(int)(test.GetRandomizer().CreateIndependent()))); + //test.Engine().AddInteraction(new NeutronDecay()); + Particle p(Proton, aZ); + p.Energy = aE*units.eV; + test.SetPrimary(p, nParticles); + test.Run(); + } +} diff --git a/src/lib/GZK.h b/src/lib/GZK.h new file mode 100644 index 0000000..732f1f0 --- /dev/null +++ b/src/lib/GZK.h @@ -0,0 +1,61 @@ +/* + * GZK.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MCRAY_GZK_H +#define MCRAY_GZK_H + +#include "Interaction.h" +#include "Background.h" + +namespace Interactions { + using namespace mcray; + + class GZK : public RandomInteractionS { + public: + GZK(BackgroundIntegral* aBackground, int aRandSeed=0); + + virtual double Rate(const Particle &aParticle) const; + + virtual RandomInteraction *Clone() const; + + virtual bool SampleS(const Particle &aParticle, double &aS, Randomizer &aRandomizer) const; + + virtual void SampleSecondaries(Particle &aParticle, std::vector<Particle> &aSecondaries, double aS, + Randomizer &aRandomizer) const; + static void UnitTest(); + static void UnitTestEconserv(double aE, double aZ); + private: + Function* InitSigma(ParticleType aPrim); + SmartPtr<BackgroundIntegral> fBackground; + SafePtr<Function> fSigmaN; + SafePtr<Function> fSigmaP; + double fMpSophia; + double fMnSophia; + }; +} + +#endif //MCRAY_GZK_H diff --git a/src/lib/GammaPP.cpp b/src/lib/GammaPP.cpp new file mode 100644 index 0000000..a2dca97 --- /dev/null +++ b/src/lib/GammaPP.cpp @@ -0,0 +1,496 @@ +/* + * GammaPP.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "GammaPP.h" +#include <math.h> +#include "MathUtils.h" +#include "Output.h" +#include "ParticleStack.h" +#include "TableBackgrounds.h" +#include "PropagationEngine.h" + +namespace Interactions { +using namespace mcray; + +const double EMInteraction::AlphaEM = 7.2973525698e-3;//~1/137 +const double EMInteraction::SigmaCoef = AlphaEM*AlphaEM*2*M_PI; + +GammaPP::GammaPP(BackgroundIntegral* aBackground): + fBackground(aBackground) +{ +} + +RandomInteraction* GammaPP::Clone() const +{ + return new GammaPP(fBackground->Clone()); +} + +GammaPP::~GammaPP() { +} + +double GammaPP::SampleSecondaryFracE(double aS, double aRand) const +{ + double mass = Particle::Mass(Electron); + double s = aS/(mass*mass); + SecondaryEnergySamplingEquationPars pars = {s, aRand*DifSigmaIntegral(0.5, s) }; + double x_lo = 0.5*(1. - sqrt(1. - 4./s)); + double x_hi = 0.5; + const double relError = 1e-3; + const int max_iter = 100; + double r = 0; + try{ + //TODO: use MathUtils::SampleLogDistribution sampling method + r = MathUtils::SolveEquation(&SecondaryEnergySamplingEquation, x_lo, x_hi, &pars, relError, max_iter, gsl_root_fsolver_bisection); + }catch(Exception* ex) + { +#ifdef _DEBUG + fDebugOutput = true; + double dx = 0.01*(x_hi-x_lo); + for(double x=x_lo; x<=x_hi; x+=dx) + { + SecondaryEnergySamplingEquation(x, &pars); + } + ASSERT(0); +#endif + throw ex; + } + return r; +} + +bool GammaPP::SampleS(const Particle& aParticle, double& aS, Randomizer& aRandomizer) const +{ + ASSERT(aParticle.Type == Photon); + aS = 0.; + return (fBackground->GetRateAndSampleS(fSigma, aParticle, aRandomizer, aS)!=0); +} + +void GammaPP::SampleSecondaries(Particle& aParticle, std::vector<Particle>& aSecondaries, double aS, Randomizer& aRandomizer) const +{ + ASSERT(aParticle.Type == Photon); + Particle sec = aParticle; + Particle sec2 = aParticle; + double rand = aRandomizer.Rand(); + sec.Type = rand>0.5 ? Electron : Positron; + sec2.Type = rand>0.5 ? Positron : Electron; + double r = SampleSecondaryFracE(aS, aRandomizer.Rand()); + sec.Energy = aParticle.Energy*r; + sec2.Energy = aParticle.Energy - sec.Energy; + aSecondaries.push_back(sec); + aSecondaries.push_back(sec2); +} + +GammaPP::Sigma::Sigma() +{ + double m = Particle::Mass(Electron); + fThresholdS=4.*m*m; +} + +double GammaPP::Sigma::f(double s) const +{ + double beta2 = 1. - fThresholdS/s; + if(beta2<=0) + return 0; + double beta = sqrt(beta2); + + double result = SigmaCoef*((3.-beta2*beta2)*log((1.+beta)/(1.-beta))-2.*beta*(2.-beta2))/s; + ASSERT_VALID_NO(result); + return result; +} + +double GammaPP::Rate(const Particle& aParticle) const +{ + if(aParticle.Type != Photon) + return 0.; + return fBackground->GetRateS(fSigma, aParticle); +} + +bool GammaPP::fDebugOutput = false; + +double GammaPP::SecondaryEnergySamplingEquation(double r, void* aParams) +{ + SecondaryEnergySamplingEquationPars* p=(SecondaryEnergySamplingEquationPars*)aParams; + double result = DifSigmaIntegral(r, p->s) - p->Integral; +#ifdef _DEBUG + if(GammaPP::fDebugOutput) + { + std::cerr << "f( " << r << " ) = " << result << std::endl; + } +#endif + return result; +} + +double GammaPP::DifSigmaIntegral(double r, double s) +{ + double beta = sqrt(1.-4./s); + double rMin = 0.5*(1.0 - beta); + if(r<=rMin) + return 0.; + return DifSigmaIntegralUnnorm(r, s) - DifSigmaIntegralUnnorm(rMin, s); +} + +double GammaPP::DifSigmaIntegralUnnorm(double r, double s) +{ +/* +Formula for unnormalized diff cross section was taken from http://lanl.arxiv.org/abs/1106.5508v1 eq. (10) +DifSigmaIntegral is integral of diff cross section from r_min = 1/2*(1 - (1 - 4/s)^(1/2)) to r +Mathematica program used to produce the output: +G[r_, s_] = 1/r*(r^2/(1 - r) + 1 - r + 4/s/(1 - r) - 4/s/s/r/(1 - r)/(1 - r))/(1 + (2 - 8/s)*4/s) +F[r_, s_] = Integrate[G[t, s], {t, 1/2*(1 - (1 - 4/s)^(1/2)), r}, Assumptions -> r > 1/2*(1 - (1 - 4/s)^(1/2)) && r < 0.5 && s > 4] +//the exact expression below has small (~1e-16 in mathematica) nonzero value for r=rMin (mathematica bug?) +//therefore we subtract F[rMin, s] from F[r, s] +*/ + double s2 = s*s; + double r2 = r*r; + double r3 = r2*r; + double beta = sqrt(1.-4./s); + double log_r_minus = log(1 - r); + double log_r = log(r); + double result = 0.; + /* + ///test (moved to DifSigmaIntegralTest) + double rMin = 0.5*(1.0 - beta); + if(r<=rMin) + return 0.; + double a=r-rMin; + if(a<1e-3) + { + double b_1=beta-1.; + double b_P1 = beta+1.; + result = (16*a*(-2 + s))/ + (b_1*b_1*b_P1*b_P1* + (-32 + 8*s + s2)) + + (32*a*a*(-4*beta + beta*s))/ + (b_1*b_1*b_1*b_P1*b_P1*b_P1* + (-32 + 8*s + s2)) + + (256*a*a*a*(28 - 11*s + s2))/ + (3.*b_1*b_1*b_1*b_1*b_P1*b_P1*b_P1*b_P1*s* + (-32 + 8*s + s2)); + } + else + {*/ + result = -((4 - 8*r + r*s2 - 3*r2*s2 + + 2*r3*s2 - 4*r*beta + + 4*r2*beta - r*s2*beta + + r2*s2*beta + 8*r*log_r_minus - + 8*r2*log_r_minus - 4*r*s*log_r_minus + + 4*r2*s*log_r_minus - r*s2*log_r_minus + + r2*s2*log_r_minus - 8*r*log_r + 8*r2*log_r + + 4*r*s*log_r - 4*r2*s*log_r + r*s2*log_r - + r2*s2*log_r + + (-1 + r)*r*(-8 + 4*s + s2)*log(1 - beta) - + (-1 + r)*r*(-8 + 4*s + s2)*log(1 + beta))/ + ((-1 + r)*r*(-32 + 8*s + s2))); + //} + ASSERT_VALID_NO(result); + return result; +} + +double GammaPP::DifSigmaIntegralTest(double r, double s, bool aSeries, double& a) +{ +/* +Formula for unnormalized diff cross section was taken from http://lanl.arxiv.org/abs/1106.5508v1 eq. (10) +DifSigmaIntegral is integral of diff cross section from r_min = 1/2*(1 - (1 - 4/s)^(1/2)) to r +Mathematica program used to produce the output: +G[r_, s_] = 1/r*(r^2/(1 - r) + 1 - r + 4/s/(1 - r) - 4/s/s/r/(1 - r)/(1 - r))/(1 + (2 - 8/s)*4/s) +F[r_, s_] = Integrate[G[t, s], {t, 1/2*(1 - (1 - 4/s)^(1/2)), r}, Assumptions -> r > 1/2*(1 - (1 - 4/s)^(1/2)) && r < 0.5 && s > 4] +//the exact expression below has small (~1e-16 in mathematica) nonzero value for r=rMin (mathematica bug?) +*/ + double s2 = s*s; + double r2 = r*r; + double r3 = r2*r; + double beta = sqrt(1.-4./s); + double log_r_minus = log(1 - r); + double log_r = log(r); + double rMin = 0.5*(1.0 - beta); + if(r<=rMin) + return 0.; + a=r-rMin; + double result = 0.; + if(aSeries)///Series[F[1/2*(1 - (1 - 4/s)^(1/2)) + a, s], {a, 0, 3}] excluding part depending on s only + { + double b_1=beta-1.; + double b_P1 = beta+1.; + result = (16*a*(-2 + s))/ + (b_1*b_1*b_P1*b_P1* + (-32 + 8*s + s2)) + + (32*a*a*(-4*beta + beta*s))/ + (b_1*b_1*b_1*b_P1*b_P1*b_P1* + (-32 + 8*s + s2)) + + (256*a*a*a*(28 - 11*s + s2))/ + (3.*b_1*b_1*b_1*b_1*b_P1*b_P1*b_P1*b_P1*s* + (-32 + 8*s + s2)); + } + else + {//F[r_, s_] + result = -((4 - 8*r + r*s2 - 3*r2*s2 + + 2*r3*s2 - 4*r*beta + + 4*r2*beta - r*s2*beta + + r2*s2*beta + 8*r*log_r_minus - + 8*r2*log_r_minus - 4*r*s*log_r_minus + + 4*r2*s*log_r_minus - r*s2*log_r_minus + + r2*s2*log_r_minus - 8*r*log_r + 8*r2*log_r + + 4*r*s*log_r - 4*r2*s*log_r + r*s2*log_r - + r2*s2*log_r + + (-1 + r)*r*(-8 + 4*s + s2)*log(1 - beta) - + (-1 + r)*r*(-8 + 4*s + s2)*log(1 + beta))/ + ((-1 + r)*r*(-32 + 8*s + s2))); + } + ASSERT_VALID_NO(result); + return result; +} + +void GammaPP::UnitTest() +{ + double Zmax = 2e-6; + double Emin = 10/units.Eunit; + //double alphaThinning = 0;//alpha = 1 conserves number of particles on the average; alpha = 0 disables thinning + ParticleStack particles; + Result result(Emin); + result.AddOutput(new SpectrumOutput("out", Emin, pow(10, 0.05))); + + cosmology.Init(); + + CompoundBackground backgr; + double cmbTemp = 2.73/Units::phTemperature_mult/units.Eunit; + + backgr.AddComponent(new PlankBackground(cmbTemp, 1e-4*cmbTemp, 1e4*cmbTemp)); + //backgr.AddComponent(new PlankBackground(cmbTemp, 6.2e-10/units.Eunit, 6.4e-10/units.Eunit));//only central value + //backgr.AddComponent(new Backgrounds::Kneiske1001EBL()); + //backgr.AddComponent(new Backgrounds::GaussianBackground(6.3e-10/units.Eunit, 1e-11/units.Eunit, 1., 413*units.Vunit)); + //double n = BackgroundUtils::CalcIntegralDensity(backgr)/units.Vunit; + + double stepZ = 0.05; + double logStepK = pow(10,0.05); + double epsRel = 1e-3; + + PBackgroundIntegral backgrI = new ContinuousBackgroundIntegral(backgr, stepZ, logStepK, Zmax, epsRel); + GammaPP* pp = new GammaPP(backgrI); + RedShift* rs = new RedShift(); + double E = Emin; + std::cerr << "#E\tRedshiftRate\tPPRate\t\t[E]=1eV [Rate]=1/Mpc\n"; + + for(double E=1; E<1e14; E*=logStepK) + { + Particle photon(Photon, 0.); + photon.Energy = E/units.Eunit;//MeV + double rate = pp->Rate(photon); + double redshiftRate = rs->Rate(photon); + double mult = 1./units.Lunit*Units::Mpc_in_cm; + std::cerr << E*1e6 << "\t" << redshiftRate*mult << "\t" << rate*mult << std::endl; + } + double s = 8.; + double x_lo = 0.5*(1. - sqrt(1. - 4./s)); + double dx = 0.01*(0.5-x_lo); + std::cerr << "\n\n\n\n\n"; + for(double x=x_lo; x<=0.5; x+=dx) + { + double a; + double seriesRes = DifSigmaIntegralTest(x,s,true,a); + double origRes = DifSigmaIntegralTest(x,s,false,a); + std::cerr << a << "\t" << origRes << "\t" << seriesRes << std::endl; + } + + + PropagationEngine pe(particles, result, 2011); + //EnergyBasedThinning thinning(alphaThinning); + //pe.SetThinning(&thinning); + pe.AddInteraction(rs); + pe.AddInteraction(pp); + + int nParticlesPerBin = 10000; + double Emax = 1e9/units.Eunit; + /// build initial state + //for(double E=Emin; E<Emax; E*=logStepK) + //{ + E=Emax; + Particle photon(Photon, Zmax); + photon.Energy = E;//MeV + //electron.Weight = exp(-Emax/E); + //if(electron.Weight>0) + for(int i=0; i<nParticlesPerBin; i++) + particles.AddPrimary(photon); + //} + std::cerr << std::setprecision(15); + pe.Run(); +} + +void GammaPP::UnitTestSampling(double aE, double aZ) +{ + std::string out = "sample_ph_E"; + out += ToString(aE*1e6);//eV + out += "_Z"; + out += ToString(aZ); + std::ofstream secPPout; + secPPout.open("sample_sec_pp",std::ios::out); + std::ofstream secPPoutVarS; + secPPoutVarS.open("sample_sec_pp_varS",std::ios::out); + + debug.SetOutputFile(out); + + if(!cosmology.IsInitialized()) + cosmology.Init(); + + CompoundBackground backgr; + double cmbTemp = 2.73/Units::phTemperature_mult/units.Eunit; + + backgr.AddComponent(new PlankBackground(cmbTemp, 1e-3*cmbTemp, 1e3*cmbTemp)); + //backgr.AddComponent(new PlankBackground(cmbTemp, 6.2e-10/units.Eunit, 6.4e-10/units.Eunit));//only central value + + double stepZ = 0.05; + double logStepK = pow(10,0.05); + double epsRel = 1e-3; + + Backgrounds::Kneiske0309EBL* kn0309 = new Backgrounds::Kneiske0309EBL(); + + backgr.AddComponent(kn0309); + + PBackgroundIntegral backgrI = new ContinuousBackgroundIntegral(backgr, stepZ, logStepK, aZ+1, epsRel); + Interactions::GammaPP pp(backgrI); + Particle photon(Photon, aZ); + photon.Energy = aE;//MeV + photon.Time.setZ(aZ); + photon.Type = Photon; + Randomizer rnd; + double S=2.; + for(int i=0; i<10000; i++) + { + std::vector<Particle> secondaries; + if(pp.GetSecondaries(photon, secondaries, rnd))//this outputs sampled s to debug file + { + ASSERT(secondaries.size()==2); + secPPoutVarS << secondaries[0].Energy/aE << "\n" << secondaries[1].Energy/aE << "\n"; + } + double r = pp.SampleSecondaryFracE(S, rnd.Rand()); + secPPout << r << '\n'; + secPPout << 1.-r << std::endl; + } + secPPout.close(); +} + +void GammaPP::UnitTest(bool aPrintRates, bool aPropagate, bool aSplit, bool aBestFitEBL) +{ + double Zmax = 2e-4; + double Emax = 3e8/units.Eunit; + double Emin = 1/units.Eunit; + //double alphaThinning = 0;//alpha = 1 conserves number of particles on the average; alpha = 0 disables thinning + ParticleStack particles; + Result result(Emin); + if(aPropagate) + { + std::string out = "out_pp_"; + out += aSplit?"split" : "no_split"; + result.AddOutput(new SpectrumOutput(out, Emin, pow(10, 0.05))); + } + if(!cosmology.IsInitialized()) + cosmology.Init(); + + CompoundBackground backgr; + double cmbTemp = 2.73/Units::phTemperature_mult/units.Eunit; + + backgr.AddComponent(new PlankBackground(cmbTemp, 1e-5*cmbTemp, 1e3*cmbTemp)); + //backgr.AddComponent(new PlankBackground(cmbTemp, 6.2e-10/units.Eunit, 6.4e-10/units.Eunit));//only central value + + double stepZ = 0.05; + double logStepK = pow(10,0.05); + double epsRel = 1e-3; + + TableBackground* ebl = aBestFitEBL ? ((TableBackground*)new Backgrounds::Kneiske0309EBL()) : + ((TableBackground*)new Backgrounds::Kneiske1001EBL()); + PBackgroundIntegral backgrIebl; + PRandomInteraction ppEbl; + if(!aSplit) + { + backgr.AddComponent(ebl); + } + else + { + backgrIebl = new ContinuousBackgroundIntegral(*ebl, stepZ, logStepK, Zmax, epsRel); + ppEbl = new GammaPP(backgrIebl); + } + + PBackgroundIntegral backgrI = new ContinuousBackgroundIntegral(backgr, stepZ, logStepK, Zmax, epsRel); + PRandomInteraction pp = new GammaPP(backgrI); + PCELInteraction rs = new RedShift(); + double E = Emin; + if(aPrintRates) + { + std::ofstream ratesOut; + std::string ratesFile = "pp_rates_"; + ratesFile += aBestFitEBL ? "BestFitEBL_" : "MinimalEBL_"; + ratesFile += aSplit ? "split" : "no_split"; + ratesOut.open(ratesFile.c_str(),std::ios::out); + + ratesOut << "#E\tRedshiftRate\tPPRate\t\t[E]=1eV [Rate]=1/Mpc\n"; + + for(double E=Emin; E<Emax; E*=logStepK) + { + Particle photon(Photon, 0.); + photon.Energy = E/units.Eunit;//MeV + double ppRate = pp->Rate(photon); + double redshiftRate = rs->Rate(photon); + if(aSplit) + { + ppRate += ppEbl->Rate(photon); + } + double mult = 1./units.Lunit*Units::Mpc_in_cm; + ratesOut << E*1e6 << "\t" << redshiftRate*mult << "\t" << ppRate*mult << std::endl; + } + } + if(!aPropagate) + return; + + PropagationEngine pe(particles, result, 2011); + //EnergyBasedThinning thinning(alphaThinning); + //pe.SetThinning(&thinning); + //pe.AddInteraction(rs); + pe.AddInteraction(pp); + //ics->fDeleteElectron = true;//check in single interaction mode + if(aSplit) + { + pe.AddInteraction(ppEbl); + } + + int nParticlesPerBin = 10000; + /// build initial state + //for(double E=Emin; E<Emax; E*=logStepK) + //{ + E=Emax; + Particle photon(Photon, Zmax); + photon.Energy = E;//MeV + //electron.Weight = exp(-Emax/E); + //if(electron.Weight>0) + for(int i=0; i<nParticlesPerBin; i++) + particles.AddPrimary(photon); + //} + + pe.RunMultithread(); + if(aSplit) + delete ebl; +} + +} /* namespace Interactions */ + diff --git a/src/lib/GammaPP.h b/src/lib/GammaPP.h new file mode 100644 index 0000000..05de267 --- /dev/null +++ b/src/lib/GammaPP.h @@ -0,0 +1,94 @@ +/* + * GammaPP.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef GAMMAPP_H_ +#define GAMMAPP_H_ + +#include "Interaction.h" +#include "Background.h" + +namespace Interactions { +using namespace mcray; + +class EMInteraction +{ +public: + static const double AlphaEM;//fine structure constant + static const double SigmaCoef;//AlphaEM*AlphaEM*2*M_PI used by PP and ICS +}; + +class GammaPP : public RandomInteractionS, EMInteraction{ + class Sigma : public Function + { + public: + Sigma(); + double f(double s) const; + double Xmin() const { return fThresholdS; } + private: + double fThresholdS; + }; +public: + GammaPP(BackgroundIntegral* aBackground); + RandomInteraction* Clone() const; + static void UnitTest(); + static void UnitTest(bool aPrintRates, bool aPropagate, bool aSplit, bool aBestFitEBL); + static void UnitTestSampling(double aE, double aZ); + virtual ~GammaPP(); + double Rate(const Particle& aParticle) const; + bool SampleS(const Particle& aParticle, double& aS, Randomizer& aRandomizer) const; + void SampleSecondaries(Particle& aParticle, std::vector<Particle>& aSecondaries, double aS, Randomizer& aRandomizer) const; + +private: + double SampleSecondaryFracE(double aS, double aRand) const; + struct SecondaryEnergySamplingEquationPars + { + double s; + double Integral; + }; + /*! + @param[in] s center of mass energy in units of electron mass squared. s>4 + @param[in] r ratio of lower energy secondary lepton energy to primary photon energy 1/2*(1 - (1 - 4/s)^(1/2)) < r < 1/2 + */ + static double DifSigmaIntegral(double r, double s); + static double DifSigmaIntegralUnnorm(double r, double s); + static double DifSigmaIntegralTest(double r, double s, bool aSeries, double& a); + + /*! + @param[in] r ratio of lower energy secondary lepton energy to primary photon energy + @param[in] aParams pointer to SecondaryEnergySamplingEquationPars struct + @return DifSigmaIntegral(r, aParams->s) - aParams->Integral + */ + static double SecondaryEnergySamplingEquation(double r, void* aParams); + + SmartPtr<BackgroundIntegral> fBackground; + Sigma fSigma; + static bool fDebugOutput; +}; + +} /* namespace Interactions */ + +#endif /* GAMMAPP_H_ */ diff --git a/src/lib/ICS.cpp b/src/lib/ICS.cpp new file mode 100644 index 0000000..6973276 --- /dev/null +++ b/src/lib/ICS.cpp @@ -0,0 +1,665 @@ +/* + * ICS.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "ICS.h" +#include <math.h> +#include "MathUtils.h" +#include "Output.h" +#include "ParticleStack.h" +#include "TableBackgrounds.h" +#include "PropagationEngine.h" +#include "PrecisionTests.h" + +namespace Interactions { +using namespace mcray; + +ICS::ICS(BackgroundIntegral* aBackground, double aGammaEmin): + fBackground(aBackground), + fGammaEmin(aGammaEmin), + fDeleteElectron(false) +{ +} + +ICS::~ICS() { +} + +double ICS::SampleSecondaryGammaFracE(double aS, double rMin, double rand) const +{ + double mass = Particle::Mass(Electron); + double s = aS/(mass*mass); + SecondaryEnergySamplingEquationPars pars; + pars.s = s; + pars.Integral = rand*PartialSigma(rMin, s); + double x_lo = rMin; + double x_hi = 1.-1./s; + const double relError = 1e-3; + const int max_iter = 100; + double r = MathUtils::SolveEquation(&SecondaryEnergySamplingEquation, x_lo, x_hi, &pars, relError, max_iter, gsl_root_fsolver_bisection); + return r; +} + +bool ICS::SampleS(const Particle& aParticle, double& aS, Randomizer& aRandomizer) const +{ + ASSERT(aParticle.Type == Electron || aParticle.Type == Positron); + double rMin = fGammaEmin/aParticle.Energy; + Sigma sigma(rMin); + aS = 0.; + return (fBackground->GetRateAndSampleS(sigma, aParticle, aRandomizer, aS)!=0); +} + +void ICS::SampleSecondaries(Particle& aParticle, std::vector<Particle>& aSecondaries, double aS, Randomizer& aRandomizer) const +{ + double rMin = fGammaEmin/aParticle.Energy; + double r = SampleSecondaryGammaFracE(aS, rMin, aRandomizer.Rand()); + Particle secLepton = aParticle; + Particle secPhoton = aParticle; + secPhoton.Type = Photon; + secPhoton.Energy = aParticle.Energy*r; + secLepton.Energy = aParticle.Energy - secPhoton.Energy; + aSecondaries.push_back(secPhoton); + if(!fDeleteElectron) + aSecondaries.push_back(secLepton); +} + +ICS::Sigma::Sigma(double rMin) : f_rMin(rMin) +{ + double mass = Particle::Mass(Electron); + fM2 = mass*mass; + fXmin = fM2/(1.-rMin); +} + +double ICS::Sigma::f(double s) const +{ + return PartialSigma(f_rMin, s/fM2); +} + +double ICS::Rate(const Particle& aParticle) const +{ + if(aParticle.Type != Electron && aParticle.Type != Positron) + return 0.; + double rMin = fGammaEmin/aParticle.Energy; + if(rMin>=1) + return 0.; + Sigma sigma(rMin); + return fBackground->GetRateS(sigma, aParticle); +} + +double ICS::SecondaryEnergySamplingEquation(double r, void* aParams) +{ + SecondaryEnergySamplingEquationPars* p=(SecondaryEnergySamplingEquationPars*)aParams; + return PartialSigma(r, p->s) - p->Integral; +} + +double ICS::PartialSigma(double rMin, double s) +{ +/* +Formula for partial cross section was taken from http://lanl.arxiv.org/abs/1106.5508v1 eq. (9) +*/ + double m=Particle::Mass(Electron); + + //return SigmaCoef/m/m*Test::PrecisionTests::PartialSigmaAccurate(rMin,s); + + + double yMin=1./s;//s here is in units of m^2 + double yMax = 1-rMin; + if(yMin>=yMax) + { + ASSERT((yMax-yMin)/(yMax+yMin)<1e-10); + return 0.; + } + + double y_1 = 1.-yMin; + double y_1_2 = y_1*y_1; + double a=yMax-yMin; + double result = 0; + if(a>1e-3) + { + result = yMin*(yMax-yMin)/y_1*(log(yMax/yMin)/(yMax-yMin)*(1.-4.*yMin*(1.+yMin)/y_1_2)+4.*(yMin/yMax+yMin)/y_1_2+0.5*(yMax+yMin)); + ASSERT_VALID_NO(result); + } + else + { + double x=yMin; + double x2=x*x; + double x3=x2*x; + double x4=x3*x; + double x_1=x-1.;//negative + double x_1_2=x_1*x_1; + double x_1_3=x_1_2*x_1; + double a2=a*a; + double a3=a2*a; + double a4=a3*a; + double a5=a4*a; + + //result = a/x_1*(-1.-x2+a/x/x_1*(-1.-3.*x+x2-x3+a/x/x_1*(-1./3.-2.*x+x2+a/x*(0.25+2.5*x-0.75*x2+0.2*a/x*(-1.-14.*x+3*x2))))); + + result = (a4*(1 + 10*x - 3*x2))/(4.*x_1_3*x3) + + (a*(1 + x2))/(1 - x) + + (a5*(-1 - 14*x + 3*x2))/(5.*x_1_3*x4) + + (a3*(-1 - 6*x + 3*x2))/(3.*x_1_3*x2) + + (a2*(-1 - 3*x + x2 - x3))/(2.*x_1_2*x); + + ASSERT_VALID_NO(result); + } + + return SigmaCoef/m/m*result; +} + +double PartialSigmaSeries(double rMin, double s) +{ +/* +Formula for partial cross section was taken from http://lanl.arxiv.org/abs/1106.5508v1 eq. (9) +*/ + double yMin=1./s;//s here is in units of m^2 + double yMax = 1-rMin; + if(yMin>=yMax) + { + ASSERT((yMax-yMin)/(yMax+yMin)<1e-10); + return 0.; + } + + double a=yMax-yMin; + double result = 0; + { + double x=yMin; + double x2=x*x; + double x3=x2*x; + double x4=x3*x; + double x_1=x-1.;//negative + double x_1_2=x_1*x_1; + double x_1_3=x_1_2*x_1; + double a2=a*a; + double a3=a2*a; + double a4=a3*a; + double a5=a4*a; + + //result = a/x_1*(-1.-x2+a/x/x_1*(-1.-3.*x+x2-x3+a/x/x_1*(-1./3.-2.*x+x2+a/x*(0.25+2.5*x-0.75*x2+0.2*a/x*(-1.-14.*x+3*x2))))); + + result = (a4*(1 + 10*x - 3*x2))/(4.*x_1_3*x3) + + (a*(1 + x2))/(1 - x) + + (a5*(-1 - 14*x + 3*x2))/(5.*x_1_3*x4) + + (a3*(-1 - 6*x + 3*x2))/(3.*x_1_3*x2) + + (a2*(-1 - 3*x + x2 - x3))/(2.*x_1_2*x); + + ASSERT_VALID_NO(result); + } + return result; +} + +double PartialSigmaExact(double rMin, double s) +{ +/* +Formula for partial cross section was taken from http://lanl.arxiv.org/abs/1106.5508v1 eq. (9) +*/ + double yMin=1./s;//s here is in units of m^2 + double yMax = 1-rMin; + if(yMin>=yMax) + { + ASSERT((yMax-yMin)/(yMax+yMin)<1e-10); + return 0.; + } + + double y_1 = 1.-yMin; + double y_1_2 = y_1*y_1; + //double a=yMax-yMin; + double result = 0; + + result = yMin*(yMax-yMin)/y_1*(log(yMax/yMin)/(yMax-yMin)*(1.-4.*yMin*(1.+yMin)/y_1_2)+4.*(yMin/yMax+yMin)/y_1_2+0.5*(yMax+yMin)); + ASSERT_VALID_NO(result); + + return result; +} + +void ICS::UnitTestDistribution(double aEmin, double aE, double aZ) +{ + std::string out = "distrib_s_ics_Emin"; + out += ToString(aEmin*1e6);//eV + out += "_E"; + out += ToString(aE*1e6);//eV + out += "_Z"; + out += ToString(aZ); + + std::ofstream distribOut; + distribOut.open(out.c_str(),std::ios::out); + FunctionCallLoggerX<double> logger(distribOut); + + if(!cosmology.IsInitialized()) + cosmology.Init(); + + CompoundBackground backgr; + double cmbTemp = 2.73/Units::phTemperature_mult/units.Eunit; + + backgr.AddComponent(new PlankBackground(cmbTemp, 1e-3*cmbTemp, 1e3*cmbTemp)); + //backgr.AddComponent(new PlankBackground(cmbTemp, 6.2e-10/units.Eunit, 6.4e-10/units.Eunit));//only central value + + double stepZ = 0.05; + double logStepK = pow(10,0.05); + double epsRel = 1e-3; + + Backgrounds::Kneiske0309EBL* kn0309 = new Backgrounds::Kneiske0309EBL(); + PBackgroundIntegral backgrIebl; + PCELInteraction icsCelEbl,icsCelFullEbl; + PRandomInteraction icsEbl,icsFullEbl; + + backgr.AddComponent(kn0309); + + PBackgroundIntegral backgrI = new ContinuousBackgroundIntegral(backgr, stepZ, logStepK, aZ+1, epsRel); + Interactions::ICS ics(backgrI,aEmin); + Particle electron(Electron, aZ); + electron.Energy = aE;//MeV + Randomizer rnd; + + std::vector<Particle> secondaries; + MathUtils::SetLogger(&logger); + ics.GetSecondaries(electron, secondaries, rnd);//this outputs distribution + + distribOut.close(); +} + +void ICS::UnitTestSampling(double aEmin, double aE, double aZ) +{ + std::string out = "sample_e_Emin"; + out += ToString(aEmin*1e6);//eV + out += "_E"; + out += ToString(aE*1e6);//eV + out += "_Z"; + out += ToString(aZ); + + debug.SetOutputFile(out); + + std::ofstream secICSout; + secICSout.open("sample_sec_gamma_ics",std::ios::out); + + std::ofstream secICSoutVarS; + secICSoutVarS.open("sample_sec_gamma_ics_varS",std::ios::out); + + if(!cosmology.IsInitialized()) + cosmology.Init(); + + CompoundBackground backgr; + double cmbTemp = 2.73/Units::phTemperature_mult/units.Eunit; + + backgr.AddComponent(new PlankBackground(cmbTemp, 1e-3*cmbTemp, 1e3*cmbTemp)); + //backgr.AddComponent(new PlankBackground(cmbTemp, 6.2e-10/units.Eunit, 6.4e-10/units.Eunit));//only central value + + double stepZ = 0.05; + double logStepK = pow(10,0.05); + double epsRel = 1e-3; + + Backgrounds::Kneiske0309EBL* kn0309 = new Backgrounds::Kneiske0309EBL(); + PBackgroundIntegral backgrIebl; + PCELInteraction icsCelEbl,icsCelFullEbl; + PRandomInteraction icsEbl,icsFullEbl; + + backgr.AddComponent(kn0309); + + PBackgroundIntegral backgrI = new ContinuousBackgroundIntegral(backgr, stepZ, logStepK, aZ+1, epsRel); + Interactions::ICS ics(backgrI,aEmin); + Particle electron(Electron,aZ); + + electron.Energy = aE;//MeV + + Randomizer rnd; + double S=2.; + for(int i=0; i<10000; i++) + { + std::vector<Particle> secondaries; + if(ics.GetSecondaries(electron, secondaries, rnd))//this outputs sampled s to debug file + { + ASSERT(secondaries.size()==2); + ASSERT(secondaries[0].Type == Photon); + secICSoutVarS << secondaries[0].Energy/aE << "\n"; + } + double r = ics.SampleSecondaryGammaFracE(S, aEmin/aE, rnd.Rand()); + secICSout << r << "\n"; + } + secICSout.close(); +} + +void ICS::UnitTest(bool aPrintRates, bool aPropagate, bool aSplit, double Emin, double Emax, double Zmax) +{ + //double alphaThinning = 0;//alpha = 1 conserves number of particles on the average; alpha = 0 disables thinning + ParticleStack particles; + Result result(Emin); + if(aPropagate) + { + std::string out = "ics_"; + out += aSplit?"split" : "no_split"; + out += "_Emin"; + out += ToString(Emin); + out += "_Emax"; + out += ToString(Emax); + out += "_Zmax"; + out += ToString(Zmax); + result.AddOutput(new SpectrumOutput("out_" + out, Emin, pow(10, 0.05))); + debug.SetOutputFile("debug_" + out); + } + if(!cosmology.IsInitialized()) + cosmology.Init(); + + CompoundBackground backgr; + double cmbTemp = 2.73/Units::phTemperature_mult/units.Eunit; + + backgr.AddComponent(new PlankBackground(cmbTemp, 1e-3*cmbTemp, 1e3*cmbTemp)); + //backgr.AddComponent(new PlankBackground(cmbTemp, 6.2e-10/units.Eunit, 6.4e-10/units.Eunit));//only central value + + double stepZ = 0.05; + double logStepK = pow(10,0.05); + double epsRel = 1e-3; + + Backgrounds::Kneiske0309EBL* kn0309 = new Backgrounds::Kneiske0309EBL(); + PBackgroundIntegral backgrIebl; + PCELInteraction icsCelEbl,icsCelFullEbl; + PRandomInteraction icsEbl,icsFullEbl; + if(!aSplit) + { + backgr.AddComponent(kn0309); + } + else + { + backgrIebl = new ContinuousBackgroundIntegral(*kn0309, stepZ, logStepK, Zmax, epsRel); + icsCelEbl = new Interactions::IcsCEL(backgrIebl,Emin); + icsEbl = new Interactions::ICS(backgrIebl,Emin); + icsFullEbl = new Interactions::ICS(backgrIebl,0); + icsCelFullEbl = new Interactions::IcsCEL(backgrIebl,1e20); + } + + PBackgroundIntegral backgrI = new ContinuousBackgroundIntegral(backgr, stepZ, logStepK, Zmax, epsRel); + PCELInteraction icsCel = new Interactions::IcsCEL(backgrI,Emin); + PRandomInteraction ics = new Interactions::ICS(backgrI,Emin); + PRandomInteraction icsFull = new Interactions::ICS(backgrI,0); + PCELInteraction icsCelFull = new Interactions::IcsCEL(backgrI,1e20); + PCELInteraction rs = new RedShift(); + double E = Emin; + if(aPrintRates) + { + std::ofstream ratesOut; + std::string ratesFile = "ics_rates_"; + ratesFile += aSplit ? "split" : "no_split"; + ratesOut.open(ratesFile.c_str(),std::ios::out); + + ratesOut << "#E\tRedshiftRate\tIcsRate\tIcsCelRate\tIcsFullRate\tIcsCelFull\t\t[E]=1eV [Rate]=1/Mpc\n"; + + for(double E=Emin; E<Emax; E*=logStepK) + { + Particle electron(Electron, 0.); + electron.Energy = E/units.Eunit;//MeV + double celRate = icsCel->Rate(electron); + double icsRate = ics->Rate(electron); + double redshiftRate = rs->Rate(electron); + double icsFullRate = icsFull->Rate(electron); + double celFull = icsCelFull->Rate(electron); + if(aSplit) + { + celRate += icsCelEbl->Rate(electron); + icsRate += icsEbl->Rate(electron); + icsFullRate += icsFullEbl->Rate(electron); + celFull += icsCelFullEbl->Rate(electron); + } + double mult = 1./units.Lunit*Units::Mpc_in_cm; + ratesOut << E*1e6 << "\t" << redshiftRate*mult << "\t" << icsRate*mult << + "\t" << celRate*mult << "\t" << icsFullRate*mult << + "\t" << celFull*mult << std::endl; + } + } + if(!aPropagate) + return; + + PropagationEngine pe(particles, result); + //EnergyBasedThinning thinning(alphaThinning); + //pe.SetThinning(&thinning); + //pe.AddInteraction(rs); + pe.AddInteraction(ics); + //ics->fDeleteElectron = true;//check in single interaction mode + //pe.AddInteraction(icsCel); + if(aSplit) + { + //pe.AddInteraction(icsCelEbl); + pe.AddInteraction(icsEbl); + } + + int nParticlesPerBin = 100000; + + /// build initial state + //for(double E=Emin; E<Emax; E*=logStepK) + //{ + E=Emax; + Particle electron(Electron, Zmax); + electron.Energy = E;//MeV + //electron.Weight = exp(-Emax/E); + //if(electron.Weight>0) + for(int i=0; i<nParticlesPerBin; i++) + particles.AddPrimary(electron); + //} + + pe.RunMultithread(); + if(aSplit) + delete kn0309; +} + +void ICS::UnitTestMono(bool aMono, int aAc, bool aRatesOnly) +{ + if(!cosmology.IsInitialized()) + cosmology.Init(); + //double rMin = 0.00031827314821827185;//3.18e-4; + //double s=1.0003183787358734;//1./(0.999-rMin); + //double exactSigma=PartialSigmaExact(rMin,s); + //double seriesSigma=PartialSigmaSeries(rMin,s); + //double sigmaAc = Test::PrecisionTests::PartialSigmaAccurate(rMin,s); + + double Zmax = 1e-6; + double Emin = 10/units.Eunit; + double alphaThinning = 0;//1 conserves number of particles on the average; alpha = 0 disables thinning + double logStepK = pow(10,0.05/aAc); + double epsRel = 1e-3; + + double conc = 413*units.Vunit;//413 cm^{-3} + double centralE = 6.3e-10/units.Eunit; + ConstFunction k(centralE); + ConstFunction c(conc); + + GaussianBackground backgr(centralE, 0.1*centralE, 1., conc); + + //double concTest = BackgroundUtils::CalcIntegralDensity(backgr, epsRel)/units.Vunit; + + + double stepZ = Zmax<0.2 ? Zmax/2 : 0.1; + + PBackgroundIntegral backgrM = new MonochromaticBackgroundIntegral(c, k, logStepK, epsRel); + PBackgroundIntegral backgrC = new ContinuousBackgroundIntegral(backgr, stepZ, pow(logStepK,0.1), Zmax, epsRel); + BackgroundIntegral* backgrI = aMono ? backgrM : backgrC; + + Interactions::IcsCEL* icsCel = new Interactions::IcsCEL(backgrI,Emin); + Interactions::ICS* ics = new Interactions::ICS(backgrI,Emin); + Interactions::ICS icsFull(backgrI,0); + Interactions::IcsCEL icsCelFull(backgrI,1e20); + RedShift* rs = new RedShift(); + double E = Emin; + std::ofstream ratesOut; + std::string ratesFile = "ics_rates_"; + ratesFile += aMono ? "mono" : "gauss"; + ratesFile += "_ac" + ToString(aAc); + ratesOut.open(ratesFile.c_str(),std::ios::out); + ratesOut << "#E\tRedshiftRate\tIcsRate\tIcsCelRate\tIcsFullRate\tIcsCelFull\t\t[E]=1eV [Rate]=1/Mpc\n"; + + for(double E=Emin; E<1e8*Emin; E*=logStepK) + { + Particle electron(Electron, 0.); + electron.Energy = E/units.Eunit;//MeV + double celRate = icsCel->Rate(electron); + double icsRate = ics->Rate(electron); + double redshiftRate = rs->Rate(electron); + double icsFullRate = icsFull.Rate(electron); + double celFull = icsCelFull.Rate(electron); + double mult = 1./units.Lunit*Units::Mpc_in_cm; + ratesOut << E*1e6 << "\t" << redshiftRate*mult << "\t" << icsRate*mult << + "\t" << celRate*mult << "\t" << icsFullRate*mult << + "\t" << celFull*mult << std::endl; + } + ratesOut.close(); + if(aRatesOnly) + return; + + ParticleStack particles; + Result result(Emin); + result.AddOutput(new SpectrumOutput(aMono ? "out_ics_mono" : "out_ics_gauss", Emin, pow(10, 0.05))); + PropagationEngine pe(particles, result, 2011); + EnergyBasedThinning thinning(alphaThinning); + pe.SetThinning(&thinning); + pe.AddInteraction(rs); + pe.AddInteraction(ics); + //ics->fDeleteElectron = true;//check in single interaction mode + pe.AddInteraction(icsCel); + //pe.AddInteraction(new Interactions::GammaPP(backgrI)); + + int nParticlesPerBin = 1000; + double Emax = 1e6/units.Eunit; + /// build initial state + //for(double E=Emin; E<Emax; E*=logStepK) + //{ + E=Emax; + Particle electron(Electron, Zmax); + electron.Energy = E;//MeV + //electron.Weight = exp(-Emax/E); + //if(electron.Weight>0) + for(int i=0; i<nParticlesPerBin; i++) + particles.AddPrimary(electron); + //} + + pe.Run(); +} + +void IcsCEL::UnitTest() +{ + double Emin=1; + double step = pow(10.,0.1); + for(double E = Emin; E<10*Emin; E*=step) + { + double rMaxGamma = Emin/E; + SigmaE sigma(rMaxGamma); + std::cout << "# Emin = " << Emin << " ; E = " << E << "\n"; + sigma.Print(std::cout, 1000, true, 0, 1e5*E); + std::cout << "\n\n"; + } +} + +IcsCEL::IcsCEL(BackgroundIntegral* aBackground, double aMaxGammaE): + fBackground(aBackground), + fMaxGammaE(aMaxGammaE) +{ +} + +IcsCEL::~IcsCEL() +{ + +} + +CELInteraction* IcsCEL::Clone() const +{ + return new IcsCEL(fBackground->Clone(), fMaxGammaE); +} + +RandomInteraction* ICS::Clone() const +{ + return new ICS(fBackground->Clone(), fGammaEmin); +} + +IcsCEL::SigmaE::SigmaE(double rGammaMax) +: f_rGammaMax(rGammaMax) +{ + f_m2 = Particle::Mass(Electron); + f_m2*=f_m2; +} + +double IcsCEL::SigmaE::f(double sDim) const +{ + double result = 0; + double s=sDim/f_m2;//s in units of m^2 + double s_1 = s-1.; + if(s_1<=0) + return 0.; + double a = 1.-1./s; + if(a>f_rGammaMax) + { + a = f_rGammaMax; + double a2=a*a; + double s_1_2 = s_1*s_1; +// Expression below is obtained from output of Mathematica: +// F[s_, r_] := (r + 1/r + 4/(s - 1)*(1 - 1/r) + 4/(s - 1)^2*(1 - 1/r)^2) +// Integrate[F[s, r]*(1 - r), {r, 1 - a, 1}, Assumptions -> a < 1 && a > 0] +// F[s,r] is expression inside brackets in formula (23) of astro-ph/9604098 + if(a<1e-3) + { + double s2=s*s; + result = a2*(1.+a/s_1*(-4./3.+a/s_1*(0.25*(9.-6.*s+s2)+0.2*a*(13.-6.*s+s2)+a2/6.*(17.-6.*s+s2)))); + } + else + { + result = -((a*(2.*a2*a*s_1_2 - 6.*(-7. + s)*(1 + s) + + 3.*a*(-5. + s*(-10. + 3.*s)) - a2*(5. + s*(2. + 5.*s))) + + 6.*(-1 + a)*(-7. + s)*(1 + s)*log(1. - a))/(6.*(-1 + a)*s_1_2)); + } + ASSERT_VALID_NO(result); + } + else + { + // Expression below is obtained from output of Mathematica: + // F[s_, r_] := (r + 1/r + 4/(s - 1)*(1 - 1/r) + 4/(s - 1)^2*(1 - 1/r)^2) + // Integrate[F[s, r]*(1 - r), {r, 1/s, 1}, Assumptions -> s > 1] + // F[s,r] is expression inside brackets in formula (23) of astro-ph/9604098 + if(s_1<1e-3) + { + result = s_1*s_1*(2./3. + s_1*(-7./5.+s_1*(49./20.-404./105.*s_1))); + } + else + { + result = -1.*(((s_1*(2. + s*(-5. + s*(-3. + s*(-71. + 5.*s)))))/(6.*s*s*s) - + (-7 + s)*(1 + s)*log(s))/s_1/s_1); + } + ASSERT_VALID_NO(result); + } + result *= (SigmaCoef/(sDim-f_m2)); + ASSERT_VALID_NO(result); + return result; +} + +double IcsCEL::SigmaE::Xmin() const +{ + return f_m2; +} + +double IcsCEL::Rate(const Particle& aParticle) const +{ + if(aParticle.Type != Electron && aParticle.Type != Positron) + return 0.; + + double rMaxGamma = fMaxGammaE/aParticle.Energy; + SigmaE sigma(rMaxGamma); + return fBackground->GetRateS(sigma, aParticle); +} + +} /* namespace Interactions */ + diff --git a/src/lib/ICS.h b/src/lib/ICS.h new file mode 100644 index 0000000..30b7769 --- /dev/null +++ b/src/lib/ICS.h @@ -0,0 +1,121 @@ +/* + * ICS.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef ICS_H_ +#define ICS_H_ +#include "Interaction.h" +#include "Background.h" +#include "GammaPP.h" + +namespace Interactions { +using namespace mcray; + +/*! + * Inverse Compton Scattering (ICS) with secondary photon energy higher than minimal is calculated in this class. + * ICS part with soft secondary photons is approximated by continuous energy loss in class IcsCEL + */ +class ICS : public RandomInteractionS, EMInteraction{ + class Sigma : public Function + { + public: + Sigma(double rMin); + double f(double s) const; + double Xmin() const {return fXmin;} + private: + double f_rMin;//minimal fraction of energy lost in single interaction + double fXmin; + double fM2; + }; +public: + ICS(BackgroundIntegral* aBackground, double aGammaEmin); + RandomInteraction* Clone() const; + virtual ~ICS(); + double Rate(const Particle& aParticle) const; + static void UnitTest(bool aPrintRates, bool aPropagate, bool aSplit, double aEmin, double aEmax, double aZmax); + static void UnitTestSampling(double aEmin, double aE, double aZ); + static void UnitTestDistribution(double aEmin, double aE, double aZ); + static void UnitTestMono(bool aMono, int aAc, bool aRatesOnly); + bool SampleS(const Particle& aParticle, double& aS, Randomizer& aRandomizer) const; + void SampleSecondaries(Particle& aParticle, std::vector<Particle>& aSecondaries, double aS, Randomizer& aRandomizer) const; + +private: + double SampleSecondaryGammaFracE(double aS, double rMin, double aRand) const; + struct SecondaryEnergySamplingEquationPars + { + double s; + double Integral; + }; + /*! + @param[in] rMin ratio of minimal secondary photon energy to primary electron energy + @param[in] s center of mass energy in units of electron mass squared + */ + static double PartialSigma(double rMin, double s); + + /*! + @param[in] r ratio of lower energy secondary lepton energy to primary photon energy + @param[in] aParams pointer to SecondaryEnergySamplingEquationPars struct + @return DifSigmaIntegral(r, aParams->s) - aParams->Integral + */ + static double SecondaryEnergySamplingEquation(double r, void* aParams); + + SmartPtr<BackgroundIntegral> fBackground; + double fGammaEmin;//minimal energy of secondary photon + bool fDeleteElectron;//used for unit test +}; + +/*! + * Continuous energy loss rate due to Inverse Compton Scattering (ICS) with soft secondary photons + * (energy lower than minimal value provided in constructor) is calculated in this class + */ +class IcsCEL : public CELInteraction, EMInteraction +{ + /*! + * 1/E * Integrate[E_gamma * dSigma/dE_gamma,{E_gamma,0,E*f_rGammaMax}] + * */ + class SigmaE : public Function + { + public: + SigmaE(double rGammaMax); + double f(double s) const; + double Xmin() const; + private: + double f_rGammaMax;//maximal fraction of energy lost in single interaction + double f_m2; + }; +public: + static void UnitTest(); + IcsCEL(BackgroundIntegral* aBackground, double aMaxGammaE); + virtual ~IcsCEL(); + double Rate(const Particle& aParticle) const; + CELInteraction* Clone() const; +private: + SmartPtr<BackgroundIntegral> fBackground; + double fMaxGammaE;//maximal energy of secondary photons +}; + +} /* namespace Interactions */ + +#endif /* ICS_H_ */ diff --git a/src/lib/Inoue12IROSpectrum.cpp b/src/lib/Inoue12IROSpectrum.cpp new file mode 100644 index 0000000..838475a --- /dev/null +++ b/src/lib/Inoue12IROSpectrum.cpp @@ -0,0 +1,60 @@ +/* + * Inoue12IROSpectrum.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "Inoue12IROSpectrum.h" + +namespace Backgrounds +{ +using namespace mcray; + +Inoue12IROSpectrum::Inoue12IROSpectrum(std::string aName, std::string aDataFile): +MatrixBackground(aName, aDataFile, false, true) +{ +} + +double Inoue12IROSpectrum::n(double aE, double z) const +{ + double E_eV = aE/units.eV; + return MatrixBackground::n(aE,z)/E_eV; +} + +Inoue12BaselineIROSpectrum::Inoue12BaselineIROSpectrum(): +Inoue12IROSpectrum("Inoue12Baseline", "EBL_Inoue/EBL_proper_baseline.dat") +{ +} + +Inoue12LowPop3IROSpectrum::Inoue12LowPop3IROSpectrum(): +Inoue12IROSpectrum("Inoue12low", "EBL_Inoue/EBL_proper_low_pop3.dat") +{ +} + +Inoue12UpperPop3IROSpectrum::Inoue12UpperPop3IROSpectrum(): +Inoue12IROSpectrum("Inoue12hi", "EBL_Inoue/EBL_proper_up_pop3.dat") +{ +} + +} diff --git a/src/lib/Inoue12IROSpectrum.h b/src/lib/Inoue12IROSpectrum.h new file mode 100644 index 0000000..753c65c --- /dev/null +++ b/src/lib/Inoue12IROSpectrum.h @@ -0,0 +1,60 @@ +/* + * Inoue12IROSpectrum.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef INOUE12IROSPECTRUM_H_ +#define INOUE12IROSPECTRUM_H_ + +#include "Background.h" + +namespace Backgrounds { + +using namespace mcray; + +class Inoue12IROSpectrum : public MatrixBackground{ +public: + Inoue12IROSpectrum(std::string aName, std::string aDataFile); + virtual double n(double E, double z) const; +}; + +class Inoue12BaselineIROSpectrum : public Inoue12IROSpectrum{ +public: + Inoue12BaselineIROSpectrum(); +}; + +class Inoue12LowPop3IROSpectrum : public Inoue12IROSpectrum{ +public: + Inoue12LowPop3IROSpectrum(); +}; + +class Inoue12UpperPop3IROSpectrum : public Inoue12IROSpectrum{ +public: + Inoue12UpperPop3IROSpectrum(); +}; + +} + +#endif /* INOUE12IROSPECTRUM_H_ */ diff --git a/src/lib/Interaction.cpp b/src/lib/Interaction.cpp new file mode 100644 index 0000000..125efde --- /dev/null +++ b/src/lib/Interaction.cpp @@ -0,0 +1,66 @@ +/* + * Interaction.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "Interaction.h" +#include "Cosmology.h" + +namespace mcray +{ +double RedShift::Rate(const Particle& aParticle) const +{ + return cosmology.eLossRate(aParticle.Time.z()); +} + +bool RandomInteractionS::GetSecondaries(Particle& aParticle, std::vector<Particle>& aSecondaries, Randomizer& aRandomizer) const +{ + double s = 0.; + if(!SampleS(aParticle, s, aRandomizer)) + return false; + debug.PrintLine(ToString(s)); + SampleSecondaries(aParticle, aSecondaries, s, aRandomizer); + return true; +} + +TypeChange::TypeChange(ParticleType aPrimary, ParticleType aSecondary, double aRate): +fPrimary(aPrimary), +fSecondary(aSecondary), +fRate(aRate){}; +double TypeChange::Rate(const Particle& aParticle) const{ + return aParticle.Type == fPrimary ? fRate : 0.; +} +bool TypeChange::GetSecondaries(Particle& aParticle, std::vector<Particle>& aSecondaries, Randomizer& aRandomizer) const{ + Particle sec = aParticle; + sec.Type = fSecondary; + sec.fCascadeProductionTime = aParticle.Time; + aSecondaries.push_back(sec); + return true; +} +RandomInteraction* TypeChange::Clone() const{ + return new TypeChange(fPrimary,fSecondary,fRate); +} + +} diff --git a/src/lib/Interaction.h b/src/lib/Interaction.h new file mode 100644 index 0000000..14b34d2 --- /dev/null +++ b/src/lib/Interaction.h @@ -0,0 +1,125 @@ +/* + * Interaction.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef INTERACTION_H +#define INTERACTION_H + +#include <vector> +#include "Particle.h" +#include "Randomizer.h" +#include "Utils.h" + +namespace mcray +{ + +///must be thread-safe +class Interaction : public Utils::SmartReferencedObj { +public: + enum InteractionType + { + IntRandom, + IntContEnergyLoss, + IntDeflections + }; + virtual ~Interaction(){}; + virtual InteractionType Type() const { return fType; }; + + ///Mean interaction rate for random processes + ///Energy loss rate 1/E dE/dt for continuous energy loss processes + ///Characteristic time^-1 for deflection process + virtual double Rate(const Particle& aParticle) const = 0; +protected: + Interaction(InteractionType aType){fType=aType;} +private: + InteractionType fType; +}; +typedef Utils::SmartPtr<Interaction> PInteraction; + +///must be thread-safe +class CELInteraction : public Interaction { +public: + CELInteraction() : Interaction(IntContEnergyLoss) { } + virtual void GetSecondaries(const Particle& aParticle, std::vector<Particle>& aSecondaries, + cosmo_time aDeltaT, Randomizer& aRandomizer) const {}; + virtual CELInteraction* Clone() const = 0; +}; +typedef Utils::SmartPtr<CELInteraction> PCELInteraction; + +///must be thread-safe +class RandomInteraction : public Interaction { +public: + RandomInteraction() : Interaction(IntRandom) { } + //returns false if the process rate is zero + virtual bool GetSecondaries(Particle& aParticle, std::vector<Particle>& aSecondaries, Randomizer& aRandomizer) const = 0; + virtual bool KillsPrimary() const + { + return true; + } + virtual RandomInteraction* Clone() const = 0; +}; +typedef Utils::SmartPtr<RandomInteraction> PRandomInteraction; + +///must be thread-safe +class DeflectionInteraction : public Interaction { +public: + DeflectionInteraction() : Interaction(IntDeflections) { } + //increases aParticle.Time by deltaT and adjust particle position and momentum direction + // Return false and leave aParticle fields unchanged if particle is subjected to the interaction + virtual bool Propagate(cosmo_time deltaT, Particle &aParticle, Randomizer &aRandomizer) const = 0; + virtual DeflectionInteraction* Clone() const = 0; +}; +typedef Utils::SmartPtr<DeflectionInteraction> PDeflectionInteraction; + +class RedShift : public CELInteraction +{ +public: + double Rate(const Particle& aParticle) const; + CELInteraction* Clone() const { return new RedShift();} +}; + +class RandomInteractionS : public RandomInteraction{ +public: + bool GetSecondaries(Particle& aParticle, std::vector<Particle>& aSecondaries, Randomizer& aRandomizer) const; + virtual bool SampleS(const Particle& aParticle, double& aS, Randomizer& aRandomizer) const = 0; + virtual void SampleSecondaries(Particle& aParticle, std::vector<Particle>& aSecondaries, double aS, Randomizer& aRandomizer) const = 0; +}; + +//Auxiliary class for debugging etc. +class TypeChange : public RandomInteraction{ + ParticleType fPrimary; + ParticleType fSecondary; + double fRate; +public: + TypeChange(ParticleType aPrimary, ParticleType aSecondary, double aRate); + virtual double Rate(const Particle& aParticle) const; + bool GetSecondaries(Particle& aParticle, std::vector<Particle>& aSecondaries, Randomizer& aRandomizer) const; + virtual RandomInteraction* Clone() const; +}; + +} +#endif /* INTERACTION_H */ + diff --git a/src/lib/Logger.cpp b/src/lib/Logger.cpp new file mode 100644 index 0000000..79de9be --- /dev/null +++ b/src/lib/Logger.cpp @@ -0,0 +1,72 @@ +/* + * Logger.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#include "Logger.h" +#include <iomanip> + +namespace mcray { + Logger::Logger(std::ostream& aOut) : + fLog(aOut), + fHeaderPrinted(false) + { + } + + void Logger::log(const Particle &p) { +#pragma omp critical (ParticleStack) + { + if(!fHeaderPrinted){ + fLog << "# id\tparent_id\t"; + print_header(fLog); + fLog << std::endl; + fHeaderPrinted = true; + } + fLog << p.id << "\t" << p.fPrevId << "\t"; + print_data(p, fLog); + fLog << std::endl; + } + } + + void Logger::print_data(const Particle &p, std::ostream& aOut){ + double t = p.Time.t()-p.SourceParticle->Time.t(); + double t_1 = 1.; + double zDif = 0.; + if(t>0){ + t_1 = 1./t; + zDif = 1.-t_1*p.X[2]; + } + aOut << p.Type << "\t"; + std::streamsize orig_pr = aOut.precision(12); + aOut << t/units.Mpc << std::setprecision(orig_pr) << "\t" << p.ElectricCharge() + << "\t" << p.Energy/units.eV << "\t" << p.Pdir[0] << "\t" << p.Pdir[1] << "\t" << t_1*p.X[0] + << "\t" << t_1*p.X[1] << "\t" << zDif; + } + + void Logger::print_header(std::ostream& aOut){ + aOut << "type\tt/Mpc\tcharge\tE/eV\tp[0]/p\tp[1]/p\tx/t\ty/t\t1-z/t"; + } +} \ No newline at end of file diff --git a/src/lib/Logger.h b/src/lib/Logger.h new file mode 100644 index 0000000..4a7749b --- /dev/null +++ b/src/lib/Logger.h @@ -0,0 +1,55 @@ +/* + * Logger.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#ifndef CRBEAM_LOGGER_H +#define CRBEAM_LOGGER_H +#include <iostream> +#include "Particle.h" + +namespace mcray { + + class ILogger { + public: + // should be thread-safe function + virtual void log(const Particle &aParticle) = 0; + }; + + class Logger : public ILogger { + std::ostream& fLog; + bool fHeaderPrinted; + public: + Logger(std::ostream& aLog); + // should be thread-safe function + virtual void log(const Particle &aParticle); + virtual void print_data(const Particle &p, std::ostream& aOut); + virtual void print_header(std::ostream& aOut); + }; + +} + +#endif //CRBEAM_LOGGER_H diff --git a/src/lib/MathUtils.cpp b/src/lib/MathUtils.cpp new file mode 100644 index 0000000..c1bf7be --- /dev/null +++ b/src/lib/MathUtils.cpp @@ -0,0 +1,1015 @@ +/* + * MathUtils.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef USE_BOOST + +#include <boost/numeric/odeint/config.hpp> + +#include <boost/numeric/odeint.hpp> +#include <boost/numeric/odeint/stepper/bulirsch_stoer.hpp> +#include <boost/numeric/odeint/stepper/bulirsch_stoer_dense_out.hpp> + +#endif + +#include "Utils.h" +#include "MathUtils.h" +#include <gsl/gsl_errno.h> +#include "gsl/gsl_sf_erf.h" +#include <gsl/gsl_math.h> +#include "TableFunction.h" +#include "nr/odeint.h" +#include "nr/stepperdopr5.h" + + +namespace Utils { +#ifdef USE_BOOST + using namespace boost::numeric::odeint; + + template< class Obj , class Mem > + class ode_wrapper + { + Obj* m_pObj; + Mem m_mem; + + public: + + ode_wrapper( Obj* obj , Mem mem ) : m_pObj(obj ) , m_mem(mem ) { } + + template< class State , class Deriv , class Time > + void operator()( const State &x , Deriv &dxdt , Time t ) + { + ((*m_pObj).*m_mem)(x , dxdt , t ); + } + }; + + template< class Obj , class Mem > + ode_wrapper< Obj , Mem > make_ode_wrapper( Obj* obj , Mem mem ) + { + return ode_wrapper< Obj , Mem >( obj , mem ); + } + + + template< class Obj , class Mem > + class observer_wrapper + { + Obj* m_pObj; + Mem m_mem; + + public: + + observer_wrapper( Obj* obj , Mem mem ) : m_pObj(obj ) , m_mem(mem ) { } + + template< class State , class Time > + void operator()( const State &x , Time t ) + { + ((*m_pObj).*m_mem)(x , t ); + } + }; + + template< class Obj , class Mem > + observer_wrapper< Obj , Mem > make_observer_wrapper( Obj* obj , Mem mem ) + { + return observer_wrapper< Obj , Mem >( obj , mem ); + } + + template< class X=double > + class Sampler4 : public ISampler<X> { + //typedef runge_kutta_dopri5<X> dopri5_type; + //typedef controlled_runge_kutta< dopri5_type > controlled_dopri5_type; + //typedef dense_output_runge_kutta< controlled_dopri5_type > dense_output_dopri5_type; + //typedef bulirsch_stoer_dense_out<X> dopri5_type; + public: + Sampler4(){ + fIntermediateVals=new std::vector<X>; + fIntermediateTs=new std::vector<X>; + fIntermediateTs->reserve(256); + fIntermediateVals->reserve(256); + } + + ~Sampler4(){ + delete fIntermediateVals; + delete fIntermediateTs; + } + + void System(const X &x , X &dxdt , X t ){ + dxdt=fFunc->f(t); + } + + void Output(const X &x , X t ){ + if(fMaxX>0 && x>=fMaxX){ + X lastT = *(fIntermediateTs->end()-1); + X lastX = *(fIntermediateVals->end()-1); + if((t-lastT)/(t+lastT)<fRelErr && t-lastT<fAbsErr){ + X result = lastT + (t-lastT)/(x-lastX)*(fMaxX-lastX); + throw result; + } + X totRate = x-lastX; + throw sample(*fFunc, lastT, t, (fMaxX-lastX)/(x-lastX), totRate, fRelErr, fAbsErr); + } + else { + fIntermediateVals->push_back(x); + fIntermediateTs->push_back(t); + } + } + + /// If total rate is known it should be passed via aTotRate argument + /// Otherwize aTotRate should be set to 0 (it will be calculated by function) + X sample(const FunctionX<X>& f, X aTmin, X aTmax, X aRand, + X & aTotRate, X aRelErr, X aAbsErr = 1e300){ + fMaxX = aTotRate*aRand; + fRelErr = aRelErr; + fAbsErr = aAbsErr; + + size_t nPoints = (size_t)((aTmax-aTmin)/aRelErr+0.5)/2+1; + X step= (aTmax-aTmin)/nPoints; + + fIntermediateTs->resize(0);//this will not decrease capacity of vectors + fIntermediateVals->resize(0); + + X curT=aTmin; + fFunc = &f; + aTotRate = 0.; + + X dt = (aTmax - aTmin) * aRelErr; + + try { + //dense_output_dopri5_type dopri5 = make_dense_output( aAbsErr , aRelErr , dopri5_type() ); + bulirsch_stoer_dense_out< X > stepper( aAbsErr , aRelErr); + integrate_adaptive( stepper , make_ode_wrapper(this,&Sampler4::System) , aTotRate , aTmin , aTmax , dt , make_observer_wrapper(this, &Sampler4::Output) ); + //integrate_const + //integrate_adaptive(dopri5, make_ode_wrapper(this,&Sampler3::System), aTotRate, aTmin, aTmax, dt, + //make_observer_wrapper(this, &Sampler3::Output)); + }catch (X aValue){ + return aValue; + } + if(aTotRate<=0){ + return aTmax; + } + std::vector<X>& intermediateVals = *fIntermediateVals; + std::vector<X>& intermediateTs = *fIntermediateTs; + X searchVal = aTotRate * aRand; + size_t i2= intermediateVals.size(); + size_t i1=0; + for(size_t i=(i2+i1)/2; i2-i1>1; i=(i2+i1)/2) + { + if(intermediateVals[i] < searchVal) + i1 = i; + else + i2 = i; + } + X t1 = intermediateTs[i1]; + X t2 = intermediateTs[i2]; + X x1 = intermediateVals[i1]; + X x2 = intermediateVals[i2]; + if((t2-t1)/(t2+t1)<fRelErr && t2-t1<fAbsErr){ + return t1 + (t2-t1)/(x2-x1)*(searchVal-x1); + } + X totRate=x2-x1; + return sample(*fFunc, t1, t2, (searchVal-x1)/(x2-x1), totRate, fRelErr, fAbsErr); + } + private: + const FunctionX<X>* fFunc; + std::vector<X>* fIntermediateVals; + std::vector<X>* fIntermediateTs; + X fMaxX; + X fRelErr; + X fAbsErr; + }; + + template< class X=double > + class Sampler3 : public ISampler<X> { + typedef runge_kutta_dopri5<X> dopri5_type; + typedef controlled_runge_kutta< dopri5_type > controlled_dopri5_type; + typedef dense_output_runge_kutta< controlled_dopri5_type > dense_output_dopri5_type; + public: + Sampler3(){ + fIntermediateVals=new std::vector<X>; + fIntermediateTs=new std::vector<X>; + fIntermediateTs->reserve(256); + fIntermediateVals->reserve(256); + } + + ~Sampler3(){ + delete fIntermediateVals; + delete fIntermediateTs; + } + + void System(const X &x , X &dxdt , X t ){ + dxdt=fFunc->f(t); + } + + void Output(const X &x , X t ){ + if(fMaxX>0 && x>=fMaxX){ + X lastT = *(fIntermediateTs->end()-1); + X lastX = *(fIntermediateVals->end()-1); + if((t-lastT)/(t+lastT)<fRelErr && t-lastT<fAbsErr){ + X result = lastT + (t-lastT)/(x-lastX)*(fMaxX-lastX); + throw result; + } + X totRate = x-lastX; + throw sample(*fFunc, lastT, t, (fMaxX-lastX)/(x-lastX), totRate, fRelErr, fAbsErr); + } + else { + fIntermediateVals->push_back(x); + fIntermediateTs->push_back(t); + } + } + + /// If total rate is known it should be passed via aTotRate argument + /// Otherwize aTotRate should be set to 0 (it will be calculated by function) + X sample(const FunctionX<X>& f, X aTmin, X aTmax, X aRand, + X & aTotRate, X aRelErr, X aAbsErr = 1e300){ + fMaxX = aTotRate*aRand; + fRelErr = aRelErr; + fAbsErr = aAbsErr; + + size_t nPoints = (size_t)((aTmax-aTmin)/aRelErr+0.5)/2+1; + X step= (aTmax-aTmin)/nPoints; + + fIntermediateTs->resize(0);//this will not decrease capacity of vectors + fIntermediateVals->resize(0); + + X curT=aTmin; + fFunc = &f; + aTotRate = 0.; + dense_output_dopri5_type dopri5 = make_dense_output( aAbsErr , aRelErr , dopri5_type() ); + X dt = (aTmax - aTmin) * aRelErr; + + try { + integrate_adaptive(dopri5, make_ode_wrapper(this,&Sampler3::System), aTotRate, aTmin, aTmax, dt, + make_observer_wrapper(this, &Sampler3::Output)); + }catch (X aValue){ + return aValue; + } + if(aTotRate<=0){ + return aTmax; + } + std::vector<X>& intermediateVals = *fIntermediateVals; + std::vector<X>& intermediateTs = *fIntermediateTs; + X searchVal = aTotRate * aRand; + size_t i2= intermediateVals.size(); + size_t i1=0; + for(size_t i=(i2+i1)/2; i2-i1>1; i=(i2+i1)/2) + { + if(intermediateVals[i] < searchVal) + i1 = i; + else + i2 = i; + } + X t1 = intermediateTs[i1]; + X t2 = intermediateTs[i2]; + X x1 = intermediateVals[i1]; + X x2 = intermediateVals[i2]; + if((t2-t1)/(t2+t1)<fRelErr && t2-t1<fAbsErr){ + return t1 + (t2-t1)/(x2-x1)*(searchVal-x1); + } + X totRate=x2-x1; + return sample(*fFunc, t1, t2, (searchVal-x1)/(x2-x1), totRate, fRelErr, fAbsErr); + } + private: + const FunctionX<X>* fFunc; + std::vector<X>* fIntermediateVals; + std::vector<X>* fIntermediateTs; + X fMaxX; + X fRelErr; + X fAbsErr; + }; + + template<typename X = double > + class Sampler : public ISampler<X> { + typedef runge_kutta_dopri5<X> dopri5_type; + typedef controlled_runge_kutta< dopri5_type > controlled_dopri5_type; + typedef dense_output_runge_kutta< controlled_dopri5_type > dense_output_dopri5_type; + public: + Sampler(){ + fIntermediateVals=0; + fIntermediateTs=0; + } + void operator()(const X &x , X &dxdt , const X t ){ + dxdt=fFunc->f(t); + } + void operator()(const X &x , const X t ){ + (*fIntermediateVals)[fLastStep++]=x; + double a=(*fIntermediateVals)[fLastStep-1]; + a=a+1; + } + + X sample(const FunctionX<X>& f, X aTmin, X aTmax, X aRand, + X & aTotRate, X aRelErr, X aAbsErr = 1e300){ + // create a vector with observation time points + size_t nPoints = (size_t)((aTmax-aTmin)/aRelErr/2+0.5); + X step= (aTmax-aTmin)/nPoints; + std::vector<X> intermediateVals(nPoints + 1); + std::vector<X> intermediateTs(nPoints + 1); + fIntermediateVals=&intermediateVals; + fIntermediateTs=&intermediateTs; + X curT=aTmin; + for( size_t i=0 ; i<=nPoints ; ++i, curT+=step ) + intermediateTs[i] = curT; + + fFunc = &f; + aTotRate = 0.; + dense_output_dopri5_type dopri5 = make_dense_output( aAbsErr , aRelErr , dopri5_type() ); + X dt = (aTmax - aTmin) * aRelErr; + + fLastStep=0; + integrate_times(dopri5 , (*this) , aTotRate , intermediateTs, dt , (*this) ); + if(aTotRate==0.){ + //aTotRate=0.; + return aTmax; + } + + X searchVal = aTotRate * aRand; + size_t i2= intermediateVals.size(); + size_t i1=0; + for(size_t i=(i2+i1)/2; i2-i1>1; i=(i2+i1)/2) + { + if(intermediateVals[i] < searchVal) + i1 = i; + else + i2 = i; + } + X t1 = intermediateTs[i1]; + X t2 = intermediateTs[i2]; + X x1 = intermediateVals[i1]; + X x2 = intermediateVals[i2]; + return t1 + (t2 - t1) / (x2 - x1) * (searchVal - x1); + } + + const FunctionX<X>* fFunc; + std::vector<X>* fIntermediateVals; + std::vector<X>* fIntermediateTs; + + size_t fLastStep; + }; + + template<typename X = double > + class LogSampler : public ISampler<X> { + typedef runge_kutta_dopri5<X> dopri5_type; + typedef controlled_runge_kutta< dopri5_type > controlled_dopri5_type; + typedef dense_output_runge_kutta< controlled_dopri5_type > dense_output_dopri5_type; + public: + LogSampler(){ + fIntermediateVals=0; + fIntermediateTs=0; + } + void operator()(const X &x , X &dxdt , const X t ){ + dxdt=fFunc->f(t); + } + void operator()(const X &x , const X t ){ + (*fIntermediateVals)[fLastStep++]=x; + double a=(*fIntermediateVals)[fLastStep-1]; + a=a+1; + } + + X sample(const FunctionX<X>& f, X aTmin, X aTmax, X aRand, + X & aTotRate, X aRelErr, X aAbsErr = 1e300){ + // create a vector with observation time points + size_t nPoints = (size_t)(log(aTmax/aTmin)/log(1.0+aRelErr)/2+0.5); + X step= pow(aTmax/aTmin, 1./nPoints); + std::vector<X> intermediateVals(nPoints + 1); + std::vector<X> intermediateTs(nPoints + 1); + fIntermediateVals=&intermediateVals; + fIntermediateTs=&intermediateTs; + X curT=aTmin; + for( size_t i=0 ; i<=nPoints ; ++i, curT*=step ) + intermediateTs[i] = curT; + + fFunc = &f; + aTotRate = 0.; + dense_output_dopri5_type dopri5 = make_dense_output( aAbsErr , aRelErr , dopri5_type() ); + X dt = (aTmax - aTmin) * aRelErr; + + fLastStep=0; + integrate_times(dopri5 , (*this) , aTotRate , intermediateTs, dt , (*this) ); + if(aTotRate==0.){ + //aTotRate=0.; + return aTmax; + } + + X searchVal = aTotRate * aRand; + size_t i2= intermediateVals.size(); + size_t i1=0; + for(size_t i=(i2+i1)/2; i2-i1>1; i=(i2+i1)/2) + { + if(intermediateVals[i] < searchVal) + i1 = i; + else + i2 = i; + } + X t1 = intermediateTs[i1]; + X t2 = intermediateTs[i2]; + X x1 = intermediateVals[i1]; + X x2 = intermediateVals[i2]; + return t1 + (t2 - t1) / (x2 - x1) * (searchVal - x1); + } + + const FunctionX<X>* fFunc; + std::vector<X>* fIntermediateVals; + std::vector<X>* fIntermediateTs; + + size_t fLastStep; + }; + + template<typename X = double > + class Sampler2 : public ISampler<X> { + typedef runge_kutta_dopri5<X> dopri5_type; + typedef controlled_runge_kutta< dopri5_type > controlled_dopri5_type; + typedef dense_output_runge_kutta< controlled_dopri5_type > dense_output_dopri5_type; + public: + Sampler2(){ + fIntermediateVals=0; + fIntermediateTs=0; + } + ~Sampler2() + { + fIntermediateVals=0; + fIntermediateTs=0; + } + void operator()(const X &x , X &dxdt , const double t ){ + dxdt=fFunc->f(t); + } + void operator()(const X &x , const X t ){ + if(fMaxX>0 && x>=fMaxX){ + X lastT = *(fIntermediateTs->end()-1); + X lastX = *(fIntermediateVals->end()-1); + if((t-lastT)/(t+lastT)<fRelErr && t-lastT<fAbsErr){ + X result = lastT + (t-lastT)/(x-lastX)*(fMaxX-lastX); + throw result; + } + X totRate = x-lastX; + throw sample(*fFunc, lastT, t, (fMaxX-lastX)/(x-lastX), totRate, fRelErr, fAbsErr); + } + else { + fIntermediateVals->push_back(x); + fIntermediateTs->push_back(t); + } + } + + /// If total rate is known it should be passed via aTotRate argument + /// Otherwize aTotRate should be set to 0 (it will be calculated by function) + X sample(const FunctionX<X>& f, X aTmin, X aTmax, X aRand, + X & aTotRate, X aRelErr, X aAbsErr = 1e300){ + fMaxX = aTotRate*aRand; + //fTmin = aTmin; + //fTmax = aTmax; + //fTotRate = aTotRate; + fRelErr = aRelErr; + fAbsErr = aAbsErr; + + size_t nPoints = 128;//(size_t)((aTmax-aTmin)/aRelErr+0.5)/2+1; + //X step= (aTmax-aTmin)/nPoints; + std::vector<X> intermediateVals; + std::vector<X> intermediateTs; + intermediateTs.reserve(nPoints); + intermediateVals.reserve(nPoints); + fIntermediateVals=&intermediateVals; + fIntermediateTs=&intermediateTs; + X curT=aTmin; + fFunc = &f; + aTotRate = 0.; + + ///TODO: try different steppers + + dense_output_dopri5_type dopri5 = make_dense_output( aAbsErr , aRelErr , dopri5_type() ); + X dt = (aTmax - aTmin) * aRelErr; + + try { + integrate_adaptive(dopri5, (*this), aTotRate, aTmin, aTmax, dt, + (*this));//this will create two copies of (*this) + /* //this one is a bit slower than dense_output_dopri5_type + typedef runge_kutta_cash_karp54< X > error_stepper_type; + integrate_adaptive( make_controlled< error_stepper_type >( aAbsErr , aRelErr ) , + (*this) , aTotRate , aTmin, aTmax, dt, + (*this));*/ + + }catch (X aValue){ + return aValue; + } + if(aTotRate<=0){ + return aTmax; + } + + X searchVal = aTotRate * aRand; + size_t i2= intermediateVals.size(); + size_t i1=0; + for(size_t i=(i2+i1)/2; i2-i1>1; i=(i2+i1)/2) + { + if(intermediateVals[i] < searchVal) + i1 = i; + else + i2 = i; + } + X t1 = intermediateTs[i1]; + X t2 = intermediateTs[i2]; + X x1 = intermediateVals[i1]; + X x2 = intermediateVals[i2]; + if((t2-t1)/(t2+t1)<fRelErr && t2-t1<fAbsErr){ + return t1 + (t2-t1)/(x2-x1)*(searchVal-x1); + } + X totRate=x2-x1; + return sample(*fFunc, t1, t2, (searchVal-x1)/(x2-x1), totRate, fRelErr, fAbsErr); + } + + const FunctionX<X>* fFunc; + std::vector<X>* fIntermediateVals; + std::vector<X>* fIntermediateTs; + X fMaxX; + X fRelErr; + X fAbsErr; + }; + +#endif //#ifdef USE_BOOST + + template<typename X = double > + class NRSampler : public ISampler<X> { + X sample(const FunctionX<X>& f, X aTmin, X aTmax, X aRand, + X & aTotRate, X aRelErr, X aAbsErr = 1e300){ + X x; + MathUtils::SampleLogDistributionNR(f,aRand,x,aTotRate,aTmin,aTmax,aRelErr); + } + }; + + double MathUtils::SolveEquation( + gsl_function F, double x_lo, double x_hi, + const double relError, const int max_iter, + const gsl_root_fsolver_type *T) +{ + double r = 0; + int status; + int iter = 0; + + gsl_root_fsolver *solver = gsl_root_fsolver_alloc (T); + gsl_root_fsolver_set (solver, &F, x_lo, x_hi); + do + { + iter++; + status = gsl_root_fsolver_iterate (solver); + r = gsl_root_fsolver_root (solver); + x_lo = gsl_root_fsolver_x_lower (solver); + x_hi = gsl_root_fsolver_x_upper (solver); + status = gsl_root_test_interval (x_lo, x_hi, 0, relError); + } + while (status == GSL_CONTINUE && iter < max_iter); + ASSERT(status == GSL_SUCCESS); + gsl_root_fsolver_free (solver); + if (status != GSL_SUCCESS) + { + if(iter >= max_iter) + Exception::Throw("Failed to solve equation: maximal number of iterations achieved"); + else + Exception::Throw("Failed to solve equation: GSL status " + ToString(status)); + } + return r; +} + +double MathUtils::SolveEquation( + double (*aEquation) (double, void*), + double x_lo, double x_hi, void* aEquationPars, + const double relError, const int max_iter, + const gsl_root_fsolver_type *T) +{ + gsl_function F; + F.function = aEquation; + F.params = aEquationPars; + return SolveEquation(F, x_lo, x_hi, relError, max_iter, T); +} + +MathUtils::MathUtils(): +gslQAGintegrator(0) +{ + +} + +MathUtils::~MathUtils() +{ + if(gslQAGintegrator) + gsl_integration_workspace_free (gslQAGintegrator); +} + + template<class X> class GaussDisr : public FunctionX<X>{ + public: + X mean; + X sigma; + GaussDisr(X aMean, X aSigma):mean(aMean),sigma(aSigma){} + virtual X f(X _x) const{ + X diff = (_x-mean)/sigma; + return exp(-diff*diff*0.5)/sigma*0.398942280401433; + } + }; + + template<class X> void ISampler<X>::UnitTest() + { + + X mean = 1e10; + X sigma = 1e9; + X minX=1e9; + X maxX=1e11; + X minI = 0.5*(1+gsl_sf_erf((minX-mean)/sigma/sqrt(2.0))); + X maxI = 0.5*(1+gsl_sf_erf((maxX-mean)/sigma/sqrt(2.0))); + X totI=maxI-minI; + + GaussDisr<X> gd(mean,sigma); + X relError = 1e-6; + int nSteps = 10000; +//gnuplot command: 0.5*(1+erf((x-mean)/sigma/sqrt(2.0))) w l + + for(int i=1; i<nSteps; i++){ + X rand = 1./nSteps*i; + X totRate = 0; + X curX = sample(gd, minX, maxX, rand, totRate, relError); + X exactFrac = (0.5*(1+gsl_sf_erf((curX-mean)/sigma/sqrt(2.0)))-minI)/totI; + std::cout << curX << "\t" << rand << "\t" << exactFrac << std::endl; + } + } + + template void ISampler<double>::UnitTest(); + template void ISampler<long double>::UnitTest(); + + template<typename X> bool MathUtils::SampleLogscaleDistribution(const Function& aDistrib, double aRand, X& aOutputX, X& aOutputIntegral, int nStepsS, X xMin, X xMax, double aRelError) +{ + std::vector<X> sArray,ratesArray; + double distrXmin = aDistrib.Xmin(); + if(xMin < distrXmin) + xMin = distrXmin; + double distrXmax = aDistrib.Xmax(); + if(xMax > distrXmax) + xMax = distrXmax; + ASSERT(xMin>0. && xMin<xMax); + double stepS = pow(xMax/xMin,1./nStepsS); + + sArray.push_back(0); + ratesArray.push_back(0); + + X taleAccLimit; + RelAccuracy<X>(taleAccLimit); + taleAccLimit*=10; + int maxIntervals = (int)(0.1/aRelError + 10.5); + aOutputIntegral=0.; + double deltaLogS = log(stepS); + double s=xMin; + for(int iS=1; iS<=nStepsS; iS++) + { + double s2=s*stepS; + X rate = Integration_qag(aDistrib,s,s2,1e-300,aRelError,maxIntervals); + ASSERT_VALID_NO(rate); + if(rate==0. && aOutputIntegral==0.) + {//move Smax + sArray[0] = deltaLogS*iS; + } + else + { + if(rate==0 || (aOutputIntegral>0 && rate/aOutputIntegral < taleAccLimit)) + rate = aOutputIntegral*taleAccLimit;//avoiding zero derivative (inverse function must be defined everywhere) + aOutputIntegral += rate; + sArray.push_back(deltaLogS*iS); + ratesArray.push_back(aOutputIntegral); + } + s=s2; + } + if(aOutputIntegral>0) + {//sampling s + ASSERT(sArray.size()>1); + double randRate = aRand*aOutputIntegral; + for(int i = ratesArray.size()-1;i>=0; i--) + ratesArray[i]-=randRate; + GSLTableFunc func(sArray, ratesArray, 0, 0, gsl_interp_cspline); + func.SetAutoLimits(); + double logError = aRelError/(nStepsS*deltaLogS); + if(logError>aRelError) + logError = aRelError; + aOutputX = SolveEquation(func, 0, nStepsS*deltaLogS, logError); + aOutputX = xMin*exp(aOutputX); + ASSERT(aOutputX>=xMin && aOutputX<=xMax); + return true; + } + return false; +} + +class MathUtilsODE +{ +public: + MathUtilsODE(const Function& aDistrib):fDistrib(aDistrib) + {} + void operator() (const nr::Doub x, nr::VecDoub_I &y, nr::VecDoub_O &dydx) { + double val = fDistrib(x); + if(val!=0) + dydx[0]= val; + else + dydx[0]= 0.; + ASSERT(val > -1.6e308 && val < 1.6e308); + } +private: + const Function& fDistrib; +}; + +IFunctionCallHandlerX<double>* MathUtils::fLogger = 0; + +bool MathUtils::SampleDistribution(const Function& aDistrib, double aRand, double& aOutputX, double& aOutputIntegral, double xMin, double xMax, double aRelError) +{ + const Function* distrib = &aDistrib; + SafePtr<DebugFunctionX<double> > func; + if(fLogger) + { + func = new DebugFunctionX<double>(aDistrib, *fLogger); + distrib = func; + } + + double distrXmin = distrib->Xmin(); + if(xMin < distrXmin) + xMin = distrXmin; + double distrXmax = distrib->Xmax(); + if(xMax > distrXmax) + xMax = distrXmax; + ASSERT(xMax>xMin); + + double initialStep = aRelError*(xMax-xMin); + + MathUtilsODE d(*distrib); + + const nr::Doub atol=0.;//1.0e-3; + const nr::Doub hmin=0.0;//minimal step (can be zero) + nr::VecDoub ystart(1); + ystart[0]=0.; + nr::Output out(-1); //output is saved at every integration step + nr::Odeint<nr::StepperDopr5<MathUtilsODE> > ode(ystart,xMin,xMax,atol,aRelError,initialStep,hmin,out,d); + ode.integrate(); + aOutputIntegral = ystart[0]; + if(aOutputIntegral<=0) + return false; + aRand *= aOutputIntegral; + int i1=0; + int i2=out.count-1; + double* ysave = out.ysave[0]; + for(int i=(i1+i2)/2; i2-i1>1; i=(i1+i2)/2) + { + double y = ysave[i]; + if(y<aRand) + i1 = i; + else + i2 = i; + } + double y1=ysave[i1]; + double y2=ysave[i2]; + double x1=out.xsave[i1]; + double x2=out.xsave[i2]; + aRand -= y1; + aOutputX = x1 + (x2-x1)/(y2-y1)*aRand;//make a linear estimate of X + if(fabs((x2-x1)/aOutputX)<=aRelError) + return true; + ystart[0]=0.; + initialStep = aRelError*(aOutputX>0 ? aOutputX : (x2-x1)); + nr::Output out2(2+(int)fabs((x2-x1)/initialStep)); + + nr::Odeint<nr::StepperDopr5<MathUtilsODE> > ode2(ystart,x1,x2,atol,aRelError,initialStep,hmin,out2,d); + ode2.integrate(); + i1=0; + i2=out2.count-1; + ysave = out2.ysave[0]; + for(int i=(i1+i2)/2; i2-i1>1; i=(i1+i2)/2) + { + double y = ysave[i]; + if(y<aRand) + i1 = i; + else + i2 = i; + } + y1=ysave[i1]; + y2=ysave[i2]; + x1=out2.xsave[i1]; + x2=out2.xsave[i2]; + aOutputX = x1 + (x2-x1)/(y2-y1)*(aRand-y1);//make a linear estimate of X + return true; +} + +class MathUtilsLogODE +{ +public: + MathUtilsLogODE(const Function& aDistrib):fDistrib(aDistrib) + {} + void operator() (const nr::Doub x, nr::VecDoub_I &y, nr::VecDoub_O &dydx) { + double xx = exp(x); + double val = xx*fDistrib(xx); + if(val!=0) + dydx[0]= val; + else + dydx[0]= 0.; + ASSERT(val > -1.6e308 && val < 1.6e308); + } +private: + const Function& fDistrib; +}; + +bool MathUtils::SampleLogDistribution(const Function& aDistrib, double aRand, double& aOutputX, double& aOutputIntegral, double xMin, double xMax, double aRelError) +{ +#ifdef USE_BOOST + return SampleLogDistributionBoost(aDistrib, aRand, aOutputX, aOutputIntegral, xMin, xMax, aRelError); +#else + return SampleLogDistributionNR(aDistrib, aRand, aOutputX, aOutputIntegral, xMin, xMax, aRelError); +#endif +} + +bool MathUtils::SampleLogDistributionNR(const Function& aDistrib, double aRand, double& aOutputX, double& aOutputIntegral, double xMin, double xMax, double aRelError) +{ + ASSERT(aRelError>0 && aRelError<=0.1); + + const Function* distrib = &aDistrib; + SafePtr<DebugFunctionX<double> > func; + if(fLogger) + { + func = new DebugFunctionX<double>(aDistrib, *fLogger); + distrib = func; + } + + double distrXmin = distrib->Xmin(); + if(xMin < distrXmin) + xMin = distrXmin; + double distrXmax = distrib->Xmax(); + if(xMax > distrXmax) + xMax = distrXmax; + ASSERT(xMax>xMin && xMin>0); + xMin=log(xMin); + xMax=log(xMax); + + double initialStep = 0.5*(xMax-xMin); +// if(aRelError<initialStep) +// initialStep=aRelError; + + MathUtilsLogODE d(*distrib); + + const nr::Doub atol=0; + const nr::Doub hmin=0.0;//minimal step (can be zero) + nr::VecDoub ystart(1); + ystart[0]=0.; + nr::Output out(-1); //output is saved at every integration step + nr::Odeint<nr::StepperDopr5<MathUtilsLogODE> > ode(ystart,xMin,xMax,atol,aRelError,initialStep,hmin,out,d); + ode.integrate(); + aOutputIntegral = ystart[0]; + if(aOutputIntegral<=0) + return false; + double yRand = aRand*aOutputIntegral; + int i1=0; + int i2=out.count-1; + double* ysave = out.ysave[0]; + for(int i=(i1+i2)/2; i2-i1>1; i=(i1+i2)/2) + { + double y = ysave[i]; + if(y<yRand) + i1 = i; + else + i2 = i; + } + double y1=ysave[i1]; + double y2=ysave[i2]; + double x1=out.xsave[i1]; + double x2=out.xsave[i2]; + double yFrac = (yRand-y1)/(y2-y1); + if((x2-x1)<aRelError) + { + aOutputX = x1 + (x2-x1)*yFrac;//make a linear estimate of X in log scale + ASSERT(aOutputX>=xMin && aOutputX<=xMax); + } + else + { + ystart[0]=0.; + initialStep = 0.5*(x2-x1);//aRelError; + nr::Output out2(2+(int)fabs((x2-x1)/initialStep)); + nr::Odeint<nr::StepperDopr5<MathUtilsLogODE> > ode2(ystart,x1,x2,atol,aRelError,initialStep,hmin,out2,d); + ode2.integrate(); + yRand = ystart[0]*yFrac; + i1=0; + i2=out2.count-1; + ysave = out2.ysave[0]; + for(int i=(i1+i2)/2; i2-i1>1; i=(i1+i2)/2) + { + double y = ysave[i]; + if(y<yRand) + i1 = i; + else + i2 = i; + } + y1=ysave[i1]; + y2=ysave[i2]; + x1=out2.xsave[i1]; + x2=out2.xsave[i2]; + aOutputX = x1 + (x2-x1)/(y2-y1)*(yRand-y1);//make a linear estimate of X in log scale + ASSERT(aOutputX>=xMin && aOutputX<=xMax); + } + aOutputX = exp(aOutputX); + ASSERT_VALID_NO(aOutputX); + return true; +} + + +bool MathUtils::SampleLogDistributionBoost(const Function& aDistrib, double aRand, double& aOutputX, double& aOutputIntegral, double xMin, double xMax, double aRelError) + { +#ifdef USE_BOOST + ASSERT(aRelError>0 && aRelError<=0.1); + ASSERT(xMin>0 && xMax>xMin); + LogSampler<double> dil;//slower 25 sec + //Sampler2<double> dil;// 20 sec (todo: fix memory leaks) + //Sampler3<double> dil;// 20 sec (todo: fix memory leaks) + aOutputIntegral=0.; + + aOutputX=dil.sample(aDistrib, xMin, xMax, aRand, aOutputIntegral, aRelError); + + ASSERT_VALID_NO(aOutputX); + return true; +#else + Exception::Throw("MathUtils::SampleLogDistributionBoost boostlib support is disabled"); + return false;//avoid compiler warning +#endif + } + +template<typename X> void MathUtils::RelAccuracy(X& aOutput) +{ + NOT_IMPLEMENTED +} + +template<> void MathUtils::RelAccuracy<double>(double& aOutput) +{ + aOutput = 1e-15; +} + +template<> void MathUtils::RelAccuracy<long double>(long double& aOutput) +{ + aOutput = 1e-18L; +} + +template bool MathUtils::SampleLogscaleDistribution<double>(const Function& aDistrib, double aRand, double& aOutputX, double& aOutputIntegral, int nStepsS, double xMin, double xMax, double aRelError); +//template bool MathUtils::SampleLogscaleDistribution<long double>(const Function& aDistrib, double aRand, long double& aOutput, int nStepsS, long double xMin, long double xMax, double aRelError); + + int MathUtils::UnitTest(){ + //LogSampler<double> dil;//slower 25 sec + //Sampler2<double> dil;// 20 sec (todo: fix memory leaks) + //Sampler3<double> dil;// 20 sec (todo: fix memory leaks) + //Sampler4<double> dil; + NRSampler<double> dil; + dil.UnitTest(); + } + +double MathUtils::Integration_qag ( + gsl_function aFunction, + double aXmin, + double aXmax, + double epsabs, + double epsrel, + size_t limit, + int key) +{ + if(gslQAGintegrator==0) + gslQAGintegrator = gsl_integration_workspace_alloc (limit); + else if(gslQAGintegrator->limit < limit) + { + gsl_integration_workspace_free (gslQAGintegrator); + gslQAGintegrator = gsl_integration_workspace_alloc (limit); + } + double result, abserr; + try{ + if(epsabs==0) + epsabs = std::numeric_limits<double>::min(); + int failed = gsl_integration_qag (&aFunction, aXmin, aXmax, epsabs, epsrel, limit, key, gslQAGintegrator, &result, &abserr); + if(failed) + { + ASSERT(0); + Exception::Throw("Integration failed with code " + ToString(failed)); + } + }catch(Exception* ex) + { +#ifdef _DEBUG + GslProxyFunction f(aFunction,aXmin,aXmax); + std::cerr << "\n\n#Integration_qag debug output:" << std::endl; + bool logscale = aXmin>0 && aXmax/aXmin > 100; + f.Print(std::cerr, 100, logscale, aXmin, aXmax); + std::cerr << "\n\n#end of Integration_qag debug output" << std::endl; +#endif + throw ex; + } + return result; +} + +} /* namespace Utils */ diff --git a/src/lib/MathUtils.h b/src/lib/MathUtils.h new file mode 100644 index 0000000..f13823e --- /dev/null +++ b/src/lib/MathUtils.h @@ -0,0 +1,263 @@ +/* + * MathUtils.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MATHUTILS_H_ +#define MATHUTILS_H_ + +#include <gsl/gsl_roots.h> +#include <gsl/gsl_integration.h> +#include <iostream> +#include "Utils.h" +#include <limits> + +namespace Utils { + + +template<typename X = double > +class FunctionX +{ +public: + virtual X f(X _x) const = 0; + virtual X Xmin() const {return -std::numeric_limits<X>::max();} + virtual X Xmax() const {return std::numeric_limits<X>::max();} + inline X operator()(X _x) const {return f(_x);}; + virtual ~FunctionX(){}; + + virtual FunctionX<X>* Clone() const { NOT_IMPLEMENTED; return 0; } + + inline operator gsl_function() const + { + gsl_function result = {GslProxyFunc, (void*)this}; + return result; + } + void Print(std::ostream& aOut, int nIntervals, bool aLogScale, double aXmin=-DBL_MAX, double aXmax=DBL_MAX) const + { + if(aXmin<Xmin()) + aXmin = Xmin(); + if(aXmax>Xmax()) + aXmax = Xmax(); + ASSERT(aXmin<=aXmax); + ASSERT(aXmax<DBL_MAX); + ASSERT(aXmin>-DBL_MAX); + ASSERT(nIntervals>=1); + ASSERT((!aLogScale) || aXmin>0); + double step = aLogScale?pow(aXmax/aXmin,1./nIntervals) : (aXmax-aXmin)/nIntervals; + double x = aXmin; + for(int i=0; i<=nIntervals; i++, (x = aLogScale ? x*step : x+step) ) + aOut << x << "\t" << f(x) << "\n"; + } +private: + static double GslProxyFunc (double x, void * params) + { + const FunctionX<X>* f = (const FunctionX<X>*)params; + return f->f(x); + } +}; + +template<typename X = double > +class Function2X +{ +public: + virtual X f(X x, X y) const = 0; + virtual X MinArg(int aArgNo) const {return std::numeric_limits<X>::min();} + virtual X MaxArg(int aArgNo) const {return std::numeric_limits<X>::max();} + + virtual inline X operator()(X _x, X _y) const {return f(_x,_y);}; + virtual ~Function2X(){}; +}; + +template<typename X = double > +class IFunctionCallHandlerX +{ +public: + virtual void OnCall(const FunctionX<X>& aFunc, X aX, X aY) const = 0; +}; + +template<typename X = double > +class DebugFunctionX : public FunctionX<X> +{ +public: + DebugFunctionX(const FunctionX<X>& aOrigFunc, const IFunctionCallHandlerX<X>& aDebugger):fOrigFunc(aOrigFunc),fDebugger(aDebugger){}; + virtual X f(X _x) const + { + X y = fOrigFunc.f(_x); + fDebugger.OnCall(fOrigFunc, _x, y); + return y; + } + virtual ~DebugFunctionX(){}; + virtual FunctionX<X>* Clone() const { return new DebugFunctionX<X>(fOrigFunc,fDebugger); } +private: + const FunctionX<X>& fOrigFunc; + const IFunctionCallHandlerX<X>& fDebugger; +}; + +template<typename X = double > +class FunctionCallLoggerX : public IFunctionCallHandlerX<X> +{ +public: + FunctionCallLoggerX(std::ostream& aOutput) : fOutput(aOutput){} + virtual void OnCall(const FunctionX<X>& aFunc, X aX, X aY) const + { + ((std::ostream&)fOutput) << aX << "\t" << aY << "\n"; + } + virtual ~FunctionCallLoggerX() + { + fOutput << std::endl; + } +private: + std::ostream& fOutput; +}; + +typedef FunctionX<double> Function; +typedef Function2X<double> Function2; + +template<typename X = double > +class ParamlessFunctionX : public FunctionX<X> +{ +public: + ParamlessFunctionX(X (*aFunction)(X)):fFunction(aFunction){} + X f(X _x) const { return fFunction(_x); } +private: + X (*fFunction)(X); +}; + +typedef ParamlessFunctionX<double> ParamlessFunction; + + template<typename X = double > + class ISampler { + public: + virtual X sample(const FunctionX<X> &f, X aTmin, X aTmax, X aRand, + X &aTotRate, X aRelErr, X aAbsErr = 1e300) = 0; + void UnitTest(); + }; + +/// Mathematical functions +/// Static methods are thread-safe +/// Non-static methods are not guaranteed to be thread-safe + ///TODO: make implementation using boost odeint and dense output and get rid of nr library +class MathUtils { +public: + MathUtils(); + ~MathUtils(); + static inline double RelDifference(double aVal1, double aVal2){ + double meanNorm = 0.5*fabs(aVal1)+fabs(aVal2); + return meanNorm==0. ? 0.:fabs(aVal1-aVal2)/meanNorm; + } + + static double SolveEquation( + double (*aEquation) (double, void*), + double x_lo, double x_hi, void* aEquationPars = 0, + const double relError=1e-3, const int max_iter=100, + const gsl_root_fsolver_type *T = gsl_root_fsolver_bisection); + + static double SolveEquation( + gsl_function f, double x_lo, double x_hi, + const double relError=1e-3, const int max_iter=100, + const gsl_root_fsolver_type *T = gsl_root_fsolver_bisection); + + double Integration_qag ( + gsl_function aFunction, + double aXmin, + double aXmax, + double epsabs, + double epsrel, + size_t limit, + int key=GSL_INTEG_GAUSS15); + template<typename X> bool SampleLogscaleDistribution(const Function& aDistrib, double aRand, X& aOutputX, X& aOutputIntegral, int nStepsS, X xMin, X xMax, double aRelError); + static bool SampleDistribution(const Function& aDistrib, double aRand, double& aOutputX, double& aOutputIntegral, double xMin, double xMax, double aRelError); + static bool SampleLogDistribution(const Function& aDistrib, double aRand, double& aOutputX, double& aOutputIntegral, double xMin, double xMax, double aRelError); + + static bool SampleLogDistributionBoost(const Function& aDistrib, double aRand, double& aOutputX, double& aOutputIntegral, double xMin, double xMax, double aRelError); + static bool SampleLogDistributionNR(const Function& aDistrib, double aRand, double& aOutputX, double& aOutputIntegral, double xMin, double xMax, double aRelError); + + template<typename X> static void RelAccuracy(X& aOutput); + static void SetLogger(IFunctionCallHandlerX<double>* aLogger) { fLogger = aLogger; } + static int UnitTest(); +private: + static double GslProxySampleLogscaleDistributionFunc (double x, void * params) + { + x=exp(x); + const Function* f = (const Function*)params; + return x*f->f(x); + } + gsl_integration_workspace* gslQAGintegrator; + static IFunctionCallHandlerX<double>* fLogger; +}; + +class GslProxyFunction : public Function +{ +public: + GslProxyFunction(gsl_function aFunction, double aXmin=-DBL_MAX, double aXmax=DBL_MAX) : + fFunction(aFunction), + fXmin(aXmin), + fXmax(aXmax){}; + virtual double f(double _x) const + { + return fFunction.function(_x,fFunction.params); + } + virtual double Xmin() const {return fXmin;} + virtual double Xmax() const {return fXmax;} + Function* Clone() const + {//There is no way to clone fFunction.params + ASSERT(0); + Exception::Throw("GslProxyFunction doesn't support Clone"); + return 0; + } +private: + gsl_function fFunction; + double fXmin; + double fXmax; +}; + +template<typename X = double > +class ConstFunctionX : public FunctionX<X> +{ +public: + ConstFunctionX(X aValue, X aXmin=-std::numeric_limits<X>::max(), X aXmax=std::numeric_limits<X>::max()) : + fValue(aValue), + fXmin(aXmin), + fXmax(aXmax){}; + virtual X f(X _x) const + { + return fValue; + } + virtual X Xmin() const {return fXmin;} + virtual X Xmax() const {return fXmax;} + FunctionX<X>* Clone() const + { + return new ConstFunctionX<X>(fValue, fXmin, fXmax); + } +private: + X fValue; + X fXmin; + X fXmax; +}; + +typedef ConstFunctionX<double> ConstFunction; + +} /* namespace Utils */ +#endif /* MATHUTILS_H_ */ diff --git a/src/lib/NeutronDecay.cpp b/src/lib/NeutronDecay.cpp new file mode 100644 index 0000000..a84933c --- /dev/null +++ b/src/lib/NeutronDecay.cpp @@ -0,0 +1,101 @@ +/* + * NeutronDecay.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#include "NeutronDecay.h" +#include "Test.h" + +namespace Interactions { + using namespace mcray; + + const double NeutronDecay::Neutron_lifetime=881.5; // (sec) + NeutronDecay::NeutronDecay(): + n_decayM(Particle::Mass(Neutron)/Neutron_lifetime/units.second) + { + double Me = Particle::Mass(Electron); + double delta_m = Particle::Mass(Neutron)-Particle::Mass(Proton); + n_decay_k=0.5*(delta_m-Me*Me/delta_m); + n_decay_E=sqrt(n_decay_k*n_decay_k+Me*Me); + } + + double NeutronDecay::Rate(const Particle &aParticle) const { + if(aParticle.Type!=Neutron) + return 0; + return n_decayM/aParticle.Energy; + } + + bool NeutronDecay::GetSecondaries(Particle &aParticle, std::vector<Particle> &aSecondaries, + Randomizer &aRandomizer) const { + if(aParticle.Type!=Neutron) + return false; + + //proton + Particle proton = aParticle; + proton.Type = Proton; + proton.Energy *= Particle::Mass(Proton)/Particle::Mass(Neutron); + double gamma = aParticle.Energy/aParticle.Mass(); + double beta = aParticle.beta(); + + //electron + //sample electron momentum direction in CM frame + double angle_e = aRandomizer.Rand()*M_PI; + double cos_e = cos(angle_e); + Particle electron = aParticle; + electron.Type = Electron; + electron.Energy = gamma*(n_decay_E-beta*cos_e*n_decay_k); + + //neutrino + Particle nu = aParticle; + nu.Type = NeutrinoAE; + nu.Energy = gamma*n_decay_k*(1.0+beta*cos_e);//cos_nu=-cos_e + + aSecondaries.push_back(proton); + aSecondaries.push_back(electron); + aSecondaries.push_back(nu); + + return true; + } + + int NeutronDecay::UnitTestEconserv(){ + double aZ=0.1; + double aE=1e19; + int nParticles = 100; + Test::EConservationTest test(aZ,1e6,2015); + test.alphaThinning = 0.0;//alpha = 1 conserves number of particles on the average; alpha = 0 disables thinning + //test.Engine().AddInteraction(new GZK(test.Backgr(),(int)(test.GetRandomizer().CreateIndependent()))); + test.Engine().AddInteraction(new NeutronDecay()); + Particle p(Neutron, aZ); + p.Energy = aE*units.eV; + test.SetPrimary(p, nParticles); + test.Run(); + return 0; + } + + RandomInteraction *NeutronDecay::Clone() const { + return new NeutronDecay(); + } +} diff --git a/src/lib/NeutronDecay.h b/src/lib/NeutronDecay.h new file mode 100644 index 0000000..93e9a99 --- /dev/null +++ b/src/lib/NeutronDecay.h @@ -0,0 +1,56 @@ +/* + * NeutronDecay.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#ifndef MCRAY_NEUTRONDECAY_H +#define MCRAY_NEUTRONDECAY_H + +#include "Interaction.h" + +namespace Interactions { + using namespace mcray; + + class NeutronDecay : public RandomInteraction { + + public: + NeutronDecay(); + + virtual double Rate(const Particle &aParticle) const; + + virtual bool GetSecondaries(Particle &aParticle, std::vector<Particle> &aSecondaries, + Randomizer &aRandomizer) const; + + virtual RandomInteraction* Clone() const; + static int UnitTestEconserv(); + private: + static const double Neutron_lifetime; // neutron lifetime in it's rest frame (sec) + const double n_decayM; // neutron decay probability it's rest frame times neutron mass + double n_decay_k; // secondary leptons momenta in CM frame (assuming zero nu mass) + double n_decay_E; // electron energy in CM frame + }; +} +#endif //MCRAY_NEUTRONDECAY_H diff --git a/src/lib/Nucleus.cpp b/src/lib/Nucleus.cpp new file mode 100644 index 0000000..6967ce4 --- /dev/null +++ b/src/lib/Nucleus.cpp @@ -0,0 +1,420 @@ +/* + * Nucleus.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// +// Photodisintegration cross sections as defined in F.W. Stecker, M.H. Salamon, Astrophys.J. 512 (1999) 521-526 (astro-ph/9808110v3) +// +#include "Nucleus.h" +#include "gsl/gsl_sf_erf.h" +#include "GammaPP.h" + +#define AUTO -1 + +namespace Interactions { + using namespace Utils; + using namespace mcray; + + const double portionsHe4[] = {0.8, 0.2}; + static const TBranching branchingHe4 = {2, portionsHe4, AUTO, AUTO}; + + const double portionsBe8[] = {0, 0, 0, 2.}; + // a trick to make Be9 decay to 2He4 + n only + static const TBranching branchingBe8 = {4, portionsBe8, 0., 0.}; + + const double portionsBe9[] = {0, 0, 0, 0, 2.}; + static const TBranching branchingBe9 = {5, portionsBe9, 0., 1.}; + + const double portionsB10[] = {0.1, 0.3, 0.1, 0.1, 0.2, 0.2}; + static const TBranching branchingB10 = {6, portionsB10, AUTO, AUTO};//3.6 + + const double portionsNa23[] = {0.10, 0.35, 0.10, 0.05, 0.15, 0.045, 0.04, 0.035, 0.03, 0.025, 0.02, 0.018, 0.015, + 0.012, 0.01}; + static const TBranching branchingNa23 = {15, portionsNa23, AUTO, AUTO};//4.349 + + static const int EUnstable = -1; + +#define NON 0 +#define INF 1000 +#define UNKNOWN_K 5 +#define UNKNOWN_DELTA 5,25 +#define FAST_DECAY {1, UNKNOWN_K, INF , 5,25} +#define NO_REACTION {2,15,0.,25,25} + +// Photodisintegration cross sections as defined in Puget, J.L., F .W. Stecker & J.H. Bredekamp 1976, ApJ 205, 638 +// with threshold energies from F.W. Stecker, M.H. Salamon, Astrophys.J. 512 (1999) 521-526 (astro-ph/9808110v3) + static const TIsotope stableIsotopes[] = { + {1, 0, {NO_REACTION, NO_REACTION}, NON, NULL}, // neutron + {1, 1, {NO_REACTION, NO_REACTION}, NON, NULL}, // proton + {2, 1, {{2.2, 5, 0.97, 3, 15}, NO_REACTION}, NON, NULL}, // H2 + {3, 2, {{5.5, 13, 0.33, 18, 18}, /* NO_REACTION */{2, 15, 0.33, 13, 13}}, NON, NULL},//No threshold data for double nucleon decay of A03 in astro-ph/9808110v3 + // -> one might either use 2 MeV threshold as in Puget, J.L., F .W. Stecker & J.H. Bredekamp 1976, ApJ 205, 638 + // or just suppress the chanel. We choose the former option + {4, 2, {{19.8, 27, 0.47, 12, 12}, {26.1, 45, 0.11, 40, 40}}, 1.11, &branchingHe4}, + {5, 3, {FAST_DECAY, NO_REACTION}, NON, NULL}, + {6, 3, {{4.6, UNKNOWN_K, INF, UNKNOWN_DELTA}, FAST_DECAY}, NON, NULL}, + {7, 4, {{7.3, UNKNOWN_K, INF, UNKNOWN_DELTA}, FAST_DECAY}, NON, NULL}, + {8, 4, {NO_REACTION, NO_REACTION}, INF, &branchingBe8},// a trick to make Be9 decay to 2He4 + n only + {9, 4, {{1.7, 26, 0.67, 20, 20}, {18.9, NON, NON, NON, NON}}, 1.00, &branchingBe9}, + {10, 5, {{6.6, 25, 0.54, 11, 11}, {8.3, 25, 0.15, 11, 11}}, 1.03, &branchingB10}, + {11, 5, {{11.2, 26, 0.85, 11, 11}, {18, 26, 0.15, 11, 11}}, 1.03, &branchingB10}, + {12, 6, {{16, 23, 0.76, 6, 6}, {27.2, NON, NON, NON, NON}}, 1.06, &branchingB10}, + {13, 6, {{4.9, 23, 0.71, 8, 8}, {20.9, 27, 0.05, 8, 8}}, 1.06, &branchingB10}, + {14, 7, {{7.6, 23, 0.46, 10, 10}, {12.5, 23, 0.37, 10, 10}}, 1.07, &branchingB10}, + {15, 7, {{10.2, 23, 0.73, 10, 10}, {18.4, 23, 0.10, 10, 10}}, 1.07, &branchingB10}, + {16, 8, {{12.1, 24, 0.83, 9, 9}, {22.3, 30, 0.04, 10, 10}}, 1.10, &branchingB10}, + {17, 8, {{4.1, 24, 0.77, 9, 9}, {16.3, 29, 0.20, 10, 10}}, 1.10, &branchingB10}, + {18, 8, {{8, 24, 0.67, 9, 9}, {21.8, 29, 0.20, 10, 10}}, 1.10, &branchingB10}, + {19, 9, {{8, 23, 0.76, 14, 14}, {16, 29, 0.14, 14, 14}}, 1.10, &branchingB10}, + {20, 10, {{12.8, 22, 0.87, 12, 12}, {20.8, 26, 0.05, 8, 8}}, 1.09, &branchingB10}, + {21, 10, {{6.8, 22, 0.84, 12, 12}, {19.6, 25, 0.08, 6, 6}}, 1.09, &branchingB10}, + {22, 10, {{10.4, 22, 0.81, 12, 12}, {23.4, 21, 0.11, 4, 4}}, 1.09, &branchingB10}, + {23, 11, {{8.8, 22, 0.83, 12, 12}, {19.2, 25, 0.12, 10, 10}}, 1.09, &branchingNa23}, + {24, 12, {{11.7, 19, 0.94, 11, 11}, {20.5, 29, 0.03, 6, 6}}, 1.08, &branchingNa23}, + {25, 12, {{7.3, 23, 0.77, 9, 9}, {19, 28, 0.20, 7, 7}}, 1.08, &branchingNa23}, + {26, 12, {{11.1, 18, 0.77, 8, 8}, {23.2, 26, 0.20, 8, 8}}, 1.08, &branchingNa23}, + {27, 13, {{8.3, 21, 0.80, 8, 8}, {19.4, 29, 0.20, 12, 12}}, 1.05, &branchingNa23}, + {28, 14, {{11.6, 21, 1.01, 8, 8}, {19.9, 30, 0.02, 8, 8}}, 1.04, &branchingNa23}, + {29, 14, {{8.5, 20, 0.83, 7, 7}, {20.1, 26, 0.20, 8, 8}}, 1.04, &branchingNa23}, + {30, 14, {{10.6, 20, 0.83, 7, 7}, {22.9, 26, 0.20, 8, 8}}, 1.04, &branchingNa23}, + {31, 15, {{7.3, 21, 0.85, 8, 8}, {17.9, 29, 0.20, 12, 12}}, 1.02, &branchingNa23}, + {32, 16, {{8.9, 22, 0.97, 12, 12}, {16.2, 30, 0.10, 12, 12}}, 1.00, &branchingNa23}, + {33, 16, {{8.6, 22, 0.82, 12, 12}, {17.5, 22, 0.25, 12, 12}}, 1.00, &branchingNa23}, + {34, 16, {{10.9, 22, 0.87, 12, 12}, {20.4, 22, 0.20, 12, 12}}, 1.00, &branchingNa23}, + {35, 17, {{6.4, 20, 0.87, 7, 7}, {17.3, 26, 0.22, 10, 10}}, 1.00, &branchingNa23}, + {36, 16, {{9.9, 22, 0.82, 12, 12}, {21.5, 22, 0.25, 12, 12}}, 1.00, &branchingNa23}, + {37, 17, {{8.4, 20, 0.81, 7, 7}, {18.3, 24, 0.28, 7, 7}}, 1.00, &branchingNa23}, + {38, 18, {{10.2, 18, 0.86, 8, 8}, {18.6, 22, 0.24, 8, 8}}, 0.98, &branchingNa23}, + {39, 19, {{6.4, 20, 0.73, 7, 7}, {16.6, 25, 0.38, 12, 12}}, 0.98, &branchingNa23}, + {40, 20, {{8.3, 20, 0.84, 6, 6}, {14.7, 26, 0.28, 10, 10}}, 0.96, &branchingNa23}, + {41, 20, {{7.8, 20, 0.92, 6, 6}, {17.7, 26, 0.20, 8, 8}}, 0.96, &branchingNa23}, + {42, 20, {{10.3, 20, 1.02, 7, 7}, {18.1, 26, 0.10, 8, 8}}, 0.96, &branchingNa23}, + {43, 20, {{7.9, 20, 0.97, 8, 8}, {18.2, 26, 0.15, 8, 8}}, 0.96, &branchingNa23}, + {44, 20, {{11.1, 20, 0.92, 9, 9}, {21.6, 26, 0.20, 8, 8}}, 0.96, &branchingNa23}, + {45, 21, {{6.9, 19, 0.97, 9, 9}, {18, 26, 0.15, 8, 8}}, 0.95, &branchingNa23}, + {46, 22, {{10.3, 19, 1.03, 8, 8}, {17.2, 25, 0.10, 6, 6}}, 0.95, &branchingNa23}, + {47, 22, {{8.9, 19, 1.03, 8, 8}, {18.7, 25, 0.10, 6, 6}}, 0.95, &branchingNa23}, + {48, 22, {{9.9, 19, 1.03, 8, 8}, {24.2, 25, 0.10, 6, 6}}, 0.95, &branchingNa23}, + {49, 22, {{8.1, 19, 1.03, 8, 8}, {19.6, 25, 0.10, 6, 6}}, 0.95, &branchingNa23}, + {50, 22, {{10.9, 19, 1.03, 8, 8}, {21.8, 25, 0.10, 6, 6}}, 0.95, &branchingNa23}, + {51, 23, {{8.1, 19, 1.02, 7, 7}, {19, 25, 0.11, 6, 6}}, 0.95, &branchingNa23}, + {52, 24, {{10.5, 18, 1.08, 7, 7}, {18.6, 24, 0.05, 8, 8}}, 0.95, &branchingNa23}, + {53, 24, {{7.9, 18, 1.03, 7, 7}, {18.4, 24, 0.10, 8, 8}}, 0.95, &branchingNa23}, + {54, 24, {{9.7, 18, 0.93, 7, 7}, {20.9, 24, 0.20, 8, 8}}, 0.95, &branchingNa23}, + {55, 25, {{8.1, 18, 0.93, 7, 7}, {17.8, 23.5, 0.20, 8, 8}}, 0.95, &branchingNa23}, + {56, 26, {{10.2, 18, 0.98, 8, 8}, {18.3, 22, 0.15, 7, 7}}, 0.95, &branchingNa23}, + }; + + void CNucleus::getPhotopionMultipliers(int aA, double &aRn, double &aRp, double &aRN) { + ASSERT(aA >= 0 && aA <= 56); + const TIsotope &theIsotope = stableIsotopes[aA]; + aRN = pow((double) aA, -1. / 3.);// = A^{2/3}(cross section) / A(energy loss suppression) + aRn = ((double) (aA - theIsotope.Z)) * aRN / ((double) aA);// * N/A (fraction of neutrons) + aRp = ((double) theIsotope.Z) * aRN / ((double) aA);// * Z/A (fraction of protons) + } + + double CNucleus::PPPmultiplierR(int aA) { + const TIsotope &theIsotope = stableIsotopes[aA]; + double result = theIsotope.Z * theIsotope.Z; + result /= (double) aA; + return result; + } + + int CNucleus::getZ(ParticleType aNucleus) { + ASSERT(aNucleus >= StartNuclei - 2 && aNucleus < EndNuclei); + int no = 2 + (int) aNucleus - StartNuclei; + return stableIsotopes[no].Z; + } + + int CNucleus::getA(ParticleType aNucleus) { + ASSERT(aNucleus >= StartNuclei - 2 && aNucleus < EndNuclei); + int no = 2 + (int) aNucleus - StartNuclei; + return stableIsotopes[no].A; + } + + CPhotoDisintegrationMap::CPhotoDisintegrationMap() { + int i; + fCanals.setIndexShift(-2);// + const int NucleiCount = EndNuclei - StartNuclei; + + for (i = 0; i < NucleiCount + 2; i++) { + fSecondaryCanals.add(new CPDMSecLine()); + } + int curParticle = StartNuclei; + for (i = 2; i < NucleiCount + 2; i++, curParticle++) { + + const TIsotope &isotope = stableIsotopes[i]; + CPDMLine *line = new CPDMLine; + //if (IsEnabled(curParticle)) { + if (isotope.pd[ESingle].sigma > 0.) { + CPhotoDisintegrationCanal *c = new CPhotoDisintegrationCanal(isotope, ESingle); + line->add(c); + initSecMap(c); + } + if (isotope.pd[EDouble].sigma > 0.) { + CPhotoDisintegrationCanal *c = new CPhotoDisintegrationCanal(isotope, EDouble); + line->add(c); + initSecMap(c); + } + if (isotope.pmn > 0) { + CPhotoDisintegrationCanal *c = new CPhotoDisintegrationCanal(isotope, EMultiple); + line->add(c); + initSecMap(c); + } + //} + fCanals.add(line); + } + } + + void CPhotoDisintegrationMap::initSecMap(CPhotoDisintegrationCanal *aCanal) { + const TIsotope &isotope = aCanal->isotope(); + int maxDeltaA = aCanal->getMaxDeltaA(); + int A = isotope.A; + ASSERT(maxDeltaA < A); + double autoN = 0.; + double autoP = 0.; + for (int deltaA = 1; deltaA <= maxDeltaA; deltaA++) { + const TIsotope &aProduct = stableIsotopes[A - deltaA];//in case A=2,3 secondary canal for proton (H1) may be duplicated, but it is ok, since summary rate is correct + double rate = aCanal->getRate(deltaA); + int deltaZ = isotope.Z - aProduct.Z; + if (deltaZ < 0) {//processes with neutron emission followed by beta decay + deltaZ = 0; + } + int deltaN = deltaA - deltaZ; + autoN += deltaN * rate; + autoP += deltaZ * rate; + if (rate > 0.) { + fSecondaryCanals[A - deltaA]->add(new TPhotoDisintegrationMapEntry(aCanal, rate)); + } + } + double rateP = aCanal->getRateP(); + double rateN = aCanal->getRateN(); + if (rateP == AUTO) { + rateP = autoP; + }; + if (rateN == AUTO) { + rateN = autoN; + }; + if (rateN > 0.) { + fSecondaryCanals[0]->add(new TPhotoDisintegrationMapEntry(aCanal, rateN)); + } + if (rateP > 0.) { + fSecondaryCanals[1]->add(new TPhotoDisintegrationMapEntry(aCanal, rateP)); + } + } +/* +//reset all entries +//used to recalculate R when background changes + void CPhotoDisintegrationMap::recalculate(const CBackgroundIntegral *aBackground) { + const int NucleiCount = EndNuclei - StartNuclei; + for (int i = 2; i < NucleiCount + 2; i++) { + CPDMLine &line = *(fCanals[i]); + int jMax = line.size(); + for (int j = 0; j < jMax; j++) + line[j]->recalculate(aBackground); + } + //printValidity(56); + } + + void CPhotoDisintegrationMap::approximate(CPhotoDisintegrationMap &aMap1, CPhotoDisintegrationMap &aMap2, + double aPart) { + const int NucleiCount = EndNuclei - StartNuclei; + for (int i = 2; i < NucleiCount + 2; i++) { + CPDMLine &line = *(fCanals[i]); + CPDMLine &line1 = *(aMap1.fCanals[i]); + CPDMLine &line2 = *(aMap2.fCanals[i]); + int jMax = line.size(); + for (int j = 0; j < jMax; j++) + line[j]->approximate(*(line1[j]), *(line2[j]), aPart); + } + }*/ + + +// get list of canals contributing to nucleus A +// A=1 proton +// A=0 neutron + const CPDMSecLine *CPhotoDisintegrationMap::getIncome(int A) const { + return fSecondaryCanals[A]; + } + +// get all suppression canals for nucleus A + const CPDMLine *CPhotoDisintegrationMap::getOutcome(int A) const { + return fCanals[A]; + } + + CPhotoDisintegrationCanal::CPhotoDisintegrationCanal(const TIsotope &aIsotope, TPhotoDisintegrationChanalType aType) + : + iIsotope(aIsotope), + iType(aType), + iMeanDeltaA(0) { + double meanNucleonMassMeV = 0.5*(Particle::Mass(Proton)+Particle::Mass(Neutron)); + //const double alpha = 0.00729735307639648;//fine structure constant + iSumTRK = 2. * M_PI * M_PI * EMInteraction::AlphaEM / meanNucleonMassMeV * units.Eunit; + //double test = iSumTRK*Lunit*Lunit*Eunit*1e27;// (should be ~ 59.8 MeV * mb) + iSumTRK *= ((double) ((iIsotope.A - iIsotope.Z) * iIsotope.Z)) / ((double) iIsotope.A); + //iSumTRK *= 0.001;//test + ASSERT(iSumTRK > 0); + + if (iType == EMultiple) + iSigma = new TLinearSigma(iIsotope.pmn, iSumTRK); + else + iSigma = new TGaussianSigma(iIsotope.pd[iType], iSumTRK); + } + + + int CPhotoDisintegrationCanal::getMaxDeltaA() { + switch (iType) { + case ESingle: + return 1; + case EDouble: + return 2; + case EMultiple: + return iIsotope.branching->noOfModes; + } + return 0;//never goes here + } + + double CPhotoDisintegrationCanal::getMeanDeltaA() { + switch (iType) { + case ESingle: + return 1; + case EDouble: + return 2; + case EMultiple: { + if (iMeanDeltaA) return iMeanDeltaA; + int maxDeltaA = iIsotope.branching->noOfModes; + for (int i = 1; i <= maxDeltaA; i++) + iMeanDeltaA += iIsotope.branching->nucleiRates[i - 1] * ((double) i); + return iMeanDeltaA; + } + } + return 0;//never goes here + } + + const double CPhotoDisintegrationCanal::AutoRate = AUTO; + + void CPhotoDisintegrationCanal::getEnergyLoss(std::vector<double> &aOutput) { + /* //TODO implement if needed + aOutput.copy(iR.ptr()); + aOutput *= (getMeanDeltaA() / ((double) iIsotope.A)); + */ + } + + double CPhotoDisintegrationCanal::getRate(int aDeltaA) { + switch (iType) { + case ESingle: + return (aDeltaA == 1) ? 1. : 0.; + case EDouble: + return (aDeltaA == 2) ? 1. : 0.; + case EMultiple: + return (aDeltaA > 0 || aDeltaA <= iIsotope.branching->noOfModes) ? iIsotope.branching->nucleiRates[ + aDeltaA - 1] : 0.; + } + return 0.;//never goes here + } + + double CPhotoDisintegrationCanal::getRateP() { + return (iType != EMultiple) ? AUTO : iIsotope.branching->pRate; + } + + double CPhotoDisintegrationCanal::getRateN() { + return (iType != EMultiple) ? AUTO : iIsotope.branching->nRate; + } + + /* + void CPhotoDisintegrationCanal::recalculate(const CBackgroundIntegral *aBackground) { + const int nn = iR.size();//Ranges().nE(); + Function *sigma = NULL; + if (iType == EMultiple) + sigma = new TLinearSigma(iIsotope.pmn); + else + sigma = new TGaussianSigma(iIsotope.pd[iType]); + + double minK = sigma->Xmin(); + double maxK = sigma->Xmax(); + + for (int i = 0; i < nn; i++) { + double R = 0; + //TODO: implement if needed + //double R = iSumTRK * aBackground->calculateR(Ranges().midNucleonGamma()[i], sigma, minK, maxK); + ASSERT_VALID_NO(R); + iR[i] = R; + } + }*/ + + const double CPhotoDisintegrationCanal::TGaussianSigma::iDefaultMaxK = 30.; + //30 MeV + const double CPhotoDisintegrationCanal::TLinearSigma::iMaxK = 150.;//150MeV + +//temporary +//double gsl_sf_erf(double x){return x;} + + CPhotoDisintegrationCanal::TGaussianSigma::TGaussianSigma(const TGaussCrossSection &aCS, double aMult, bool aCompatibilityMode) : iCS(aCS) { + if(aCompatibilityMode) {//this version was originally used in propagation program + iMaxK = iCS.k + + 0.5 * + iCS.deltaPlus;//this adjustment of iMaxK is done to take into account wide deltas for light nuclei + if (iMaxK < + iDefaultMaxK)// the way it is done need to be checked, the only unclear reference is page 646 of ApJ 205 (Puget at al 1976) + iMaxK = iDefaultMaxK; + } + else + iMaxK = iDefaultMaxK; + + + double leftIntegral = + iCS.deltaMinus * sqrt(M_PI / 8.) * gsl_sf_erf(sqrt(2.) * (iCS.k - iCS.kTh) / iCS.deltaMinus); + double rightIntegral = iCS.deltaPlus * sqrt(M_PI / 8.) * gsl_sf_erf(sqrt(2.) * (iMaxK - iCS.k) / iCS.deltaPlus); + iNorm = iCS.sigma / (leftIntegral + rightIntegral)*aMult; + } + + + double CPhotoDisintegrationCanal::TGaussianSigma::f(double _x) const { + double power = (_x * units.Eunit - iCS.k); + power /= ((power < 0.) ? iCS.deltaMinus : iCS.deltaPlus); + return iNorm * exp(-2. * power * power); + } + + double CPhotoDisintegrationCanal::TGaussianSigma::Xmin() const { + return iCS.kTh / units.Eunit; + } + + double CPhotoDisintegrationCanal::TGaussianSigma::Xmax() const { + return TGaussianSigma::iMaxK / units.Eunit; + } + + CPhotoDisintegrationCanal::TLinearSigma::TLinearSigma(double aSigmaIntegral, double aMult) { + iNorm = aMult*aSigmaIntegral / (iMaxK - TGaussianSigma::iDefaultMaxK); + } + + double CPhotoDisintegrationCanal::TLinearSigma::Xmin() const { + return CPhotoDisintegrationCanal::TGaussianSigma::iDefaultMaxK / units.Eunit; + } + + double CPhotoDisintegrationCanal::TLinearSigma::Xmax() const { + return TLinearSigma::iMaxK / units.Eunit; + } +}//end of namespace Interactions \ No newline at end of file diff --git a/src/lib/Nucleus.h b/src/lib/Nucleus.h new file mode 100644 index 0000000..0535ec3 --- /dev/null +++ b/src/lib/Nucleus.h @@ -0,0 +1,215 @@ +/* + * Nucleus.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#if !defined(NUCLEUS_H__INCLUDED_) +#define NUCLEUS_H__INCLUDED_ + + +#include "Particle.h" +#include "MathUtils.h" + +namespace Interactions { + using namespace Utils; + using namespace mcray; + + class CBackgroundIntegral; + + struct TGaussCrossSection { + double kTh; + //threshold energy (MeV) + double k; //middle energy (MeV) + double sigma; //total sigma integrated + double deltaMinus; //energy range (MeV) + double deltaPlus; //energy range (MeV) + }; + + + struct TBranching { + int noOfModes; + const double *nucleiRates; + double pRate; + double nRate; + }; + + struct TIsotope { + int A; //mass + int Z; //electric charge + + // photodisintegration with single and double nucleon emission + TGaussCrossSection pd[2]; + + // photodisintegration with multiple nucleon emission + double pmn; + + // branching rates for multiple nucleon emission + const TBranching *branching; + }; + + enum TPhotoDisintegrationChanalType { + ESingle = 0, //order and numbers are important (see for ex. CPhotoDisintegrationCanal::recalculate) + EDouble, + EMultiple + }; + + class CPhotoDisintegrationCanal { + public: + CPhotoDisintegrationCanal(const TIsotope &aIsotope, TPhotoDisintegrationChanalType aType); + + Function* Sigma(){return iSigma;} + + //forces recalculation of R next time R(int) is called + //used to recalculate R when background changes + //void recalculate(const CBackgroundIntegral *aBackground); + + //void approximate(CPhotoDisintegrationCanal &aCanal1, CPhotoDisintegrationCanal &aCanal2, double aPart); + + int getMaxDeltaA(); + + double getMeanDeltaA(); + + //needed for energy loss outputs + double getRate(int aDeltaA); + + double getRateP(); + + double getRateN(); + + void getEnergyLoss(std::vector<double> &aOutput); + + inline ParticleType getParticle() const { return (ParticleType)(StartNuclei + iIsotope.A - 2); }; + + inline TPhotoDisintegrationChanalType type() { return iType; }; + + inline const TIsotope &isotope() { return iIsotope; }; + + static const double AutoRate; + + + class TGaussianSigma : public Function { + public: + TGaussianSigma(const TGaussCrossSection &aCS, double aMult, bool aCompatibilityMode=false); + + double f(double _x) const; + + //sigma devided by TRK sum + double Xmin() const; + + double Xmax() const; + + private: + double iNorm; + const TGaussCrossSection &iCS; + double iMaxK; + public: + static const double iDefaultMaxK;//30MeV + }; + + class TLinearSigma : public Function { + public: + TLinearSigma(double aSigmaIntegral, double aMult); + + double f(double _x) const { return iNorm; }; + + //sigma devided by TRK sum + double Xmin() const; + + double Xmax() const; + + private: + double iNorm; + public: + static const double iMaxK;//150MeV + }; + + const TIsotope &iIsotope; + TPhotoDisintegrationChanalType iType; + double iSumTRK; + double iMeanDeltaA; + SafePtr<Function> iSigma; + }; + + struct TPhotoDisintegrationMapEntry { + CPhotoDisintegrationCanal *iCanal; + double iRate; + + TPhotoDisintegrationMapEntry(CPhotoDisintegrationCanal *aCanal, double aRate) : iCanal(aCanal), + iRate(aRate) { }; + }; + + typedef AutoDeletePtrArray <TPhotoDisintegrationMapEntry> CPDMSecLine; + typedef AutoDeletePtrArray <CPhotoDisintegrationCanal> CPDMLine;//canals for single isotope + + + class CPhotoDisintegrationMap { + public: + CPhotoDisintegrationMap(); + + //reset all entries + //used to recalculate R when background changes + //void recalculate(const CBackgroundIntegral *aBackground); + + //void approximate(CPhotoDisintegrationMap &aMap1, CPhotoDisintegrationMap &aMap2, double aPart); + + // get list of canals contributing to nucleus A + // A=1 proton + // A=0 neutron + const CPDMSecLine *getIncome(int A) const; + + // get all suppression canals for nucleus A + const CPDMLine *getOutcome(int A) const; + + void printValidity(int aA); + +//debug + void printEnergyLoss(int aA, std::string &aFileName); + + private: + + void initSecMap(CPhotoDisintegrationCanal *aCanal); + + typedef AutoDeletePtrArray <CPDMLine> CPDMTable; + typedef AutoDeletePtrArray <CPDMSecLine> CPDMSecTable; + + CPDMTable fCanals; + CPDMSecTable fSecondaryCanals; + }; + + class CNucleus { + public: + static double PPPmultiplierR(int aA); + + static int getZ(ParticleType aNucleus); + + static int getA(ParticleType aNucleus); + + static void getPhotopionMultipliers(int aA, double &aRn, double &aRp, double &aRN); + + }; +} + +#endif // !defined(NUCLEUS_H__INCLUDED_) diff --git a/src/lib/Output.cpp b/src/lib/Output.cpp new file mode 100644 index 0000000..3eddf84 --- /dev/null +++ b/src/lib/Output.cpp @@ -0,0 +1,279 @@ +/* + * Output.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#include "Output.h" +#include<sys/stat.h> +#include <map> +#include <utility> +#include "ParticleStack.h" + +namespace mcray { + +SpectrumOutput::SpectrumOutput(std::string aFile, double aEmin, double aStep): +fEmin(aEmin), +fLogStep(log(aStep)), +fFile(aFile) +{ + fEmin /= sqrt(aStep);//center bins in Emin*aStep^N + //std::ofstream fileOut; + //fileOut.open(fFile.c_str());//make sure file can be created and delete the contents of old file if it exists + //if(!fileOut) + //Exception::Throw("Failed to open " + fFile + " for writing"); + //fileOut.close(); +} + +SpectrumOutput::~SpectrumOutput() { + +} + +void SpectrumOutput::AddParticle(const Particle& aParticle) +{ + int iBin = log(aParticle.Energy/fEmin)/fLogStep; + if(iBin<0) + return;//skip output of particles with E<Emin + Vector& spec = fSpectra[aParticle.Type]; + std::vector<int>& numbers = fNumberOfParticles[aParticle.Type]; + while((int)spec.size()<=iBin) + { + spec.push_back(0); + numbers.push_back(0); + } + spec[iBin] += aParticle.Weight; + numbers[iBin] += 1; +} + +void SpectrumOutput::Flush(bool aFinal) +{ + if(!aFinal) + return;//intermedeate output is not supported + std::ofstream fileOut; + fileOut.open(fFile.c_str());//replace previous content + //fileOut.open(fFile.c_str(), std::ios::app);//don't remove previous contents + if(!fileOut) + Exception::Throw("Failed to open " + fFile + " for writing"); + //fileOut.seekp(std::ios::beg);//every next spectrum output is saved in the beginning of the file + unsigned int nBins = 0; + for(int p=0; p<ParticleTypeEOF; p++) + { + unsigned int size = fSpectra[p].size(); + if(size>nBins) + nBins = size; + } + double mult = exp(fLogStep); + double E=fEmin*sqrt(mult); + double deltaEmult = sqrt(mult) - 1./sqrt(mult); + for(unsigned int iE=0; iE<nBins; iE++, E*=mult) + { + fileOut << E*units.Eunit*1e6;//eV + for(unsigned int p=0; p<ParticleTypeEOF; p++) + { + double intFlux = 0; + int nParticles = 0; + if(fSpectra[p].size()>iE) + { + intFlux = fSpectra[p][iE]; + nParticles = fNumberOfParticles[p][iE]; + } + fileOut << "\t" << intFlux*deltaEmult*E << "\t" << nParticles;//print dN(E)/dE * E^2 + } + fileOut << '\n'; + } + fileOut << "\n\n";/// delimiters between consequent spectra outputs + fileOut.close(); +} + +RawOutput::RawOutput(std::string aDir, bool aCheckRepetitions, bool aOverwriteExisting): + fParticles(0), + fDir(aDir), + fCheckRepetitions(aCheckRepetitions), + fHeaderWritten(ParticleTypeEOF,false), + fFlushInProgress(false) +{ + SetOutputDir(aDir, aOverwriteExisting); + fParticles = new std::vector<Particle>[ParticleTypeEOF]; +} + +RawOutput::~RawOutput() +{ + delete[] fParticles; +} + +void RawOutput::SetOutputDir(std::string aDir, bool aOverwriteExisting) +{ + fDir = aDir; + if(mkdir(fDir.c_str(),0777)) + { + if(aOverwriteExisting) + { + system(("rm -r " + aDir).c_str()); + if(!mkdir(fDir.c_str(),0777)) + return; + } + Exception::Throw("Failed to create directory " + aDir); + } +} + +void RawOutput::CopyToStack(ParticleStack& aStack) +{ + for(int p=0; p<ParticleTypeEOF; p++) + { + std::vector<Particle>& particles = fParticles[p]; + if(particles.size()) + aStack.AddSecondaries(particles); + } +} + +void RawOutput::AddParticle(const Particle& aParticle) +{ + fParticles[aParticle.Type].push_back(aParticle); +} + +void RawOutput::Flush(bool aFinal) +{ + if(fFlushInProgress) + Exception::Throw("RawOutput::Flush(): was invoked from more than one thread"); + fFlushInProgress = true; + if(fCheckRepetitions && aFinal) + LookForRepetitions(fParticles); + + + for(int p=0; p<ParticleTypeEOF; p++) + { + std::vector<Particle>& particles = fParticles[p]; + if(particles.size()==0) + continue; + std::string fileName = fDir + "/" + Particle::Name((ParticleType)p); + std::ofstream file; + file.open(fileName.c_str(), std::ios::app); + if(!file) { + fFlushInProgress = false; + Exception::Throw("Failed to open " + fileName + " for writing"); + } + std::vector<Particle>::const_iterator pit = particles.begin(); + if(!fHeaderWritten[p]) { + WriteHeader(file, *pit); + file << "\n"; + fHeaderWritten[p] = true; + } + for(; pit != particles.end(); pit++) + { + Write(file, *pit); + file << "\n"; + } + file.close(); + particles.clear(); + } + fFlushInProgress = false; +} + +void RawOutput::WriteHeader(std::ostream& aOut, const Particle& aFirstParticle){ + aOut << "# E/eV\tweight\tNinteractions\tz_fin\tsinBeta\tD_lastDeflection/Mpc\tD_source\tz_source"; +} + +void RawOutput::Write(std::ostream& aOut, const Particle& aParticle){ + double beta = aParticle.DeflectionAngle(); + double sinBeta = (beta<0.5*M_PI)?sin(aParticle.DeflectionAngle()):1.;//to enable correct filtering based on sin(beta) set sin=1 for particles deflected by angles larger than Pi/2 + double d = cosmology.z2d(aParticle.SourceParticle->Time.z())/units.Mpc; + double l = cosmology.z2d(aParticle.LastDeflectionTime().z())/units.Mpc; + aOut << aParticle.Energy*units.Eunit*1e6 << "\t" << aParticle.Weight << "\t" + << aParticle.Ninteractions << "\t" << aParticle.Time.z() << "\t" //used for debugging + << sinBeta << "\t" << l << "\t" << d << "\t" << aParticle.SourceParticle->Time.z(); //used to filter on base of PSF and jet angle or on base of source Z +} + +void RawOutput::LookForRepetitions(std::vector<Particle>* aParticles) +{ + std::map<float, int> energies; + for(int p=0; p<ParticleTypeEOF; p++) + { + std::vector<Particle>& particles = aParticles[p]; + for(std::vector<Particle>::const_iterator pit = particles.begin(); pit != particles.end(); pit++) + { + if(!pit->Ninteractions)//skipping particles which did not interact + continue; + float log10E = (float)log10(pit->Energy*units.Eunit*1e6); + energies[log10E]++; + } + } + + std::ofstream file; + for (std::map<float,int>::iterator it = energies.begin(); it != energies.end(); it++) + { + if(it->second > 1) + { + if(!file.is_open()) + { + std::cerr << "Repetitions found" << std::endl; + std::string fileName = fDir + "/repetitions"; + file.open(fileName.c_str(),std::ios::out); + if(!file.is_open()) + Exception::Throw("Failed to create " + fileName); + } + file << it->first << "\t" << it->second << "\n"; + } + } + if(file.is_open()) + file.close(); +} + + void RawOutput3D::Write(std::ostream& aOut, const Particle& aParticle){ + double T = aParticle.Time.t()-aParticle.SourceParticle->Time.t(); + aOut << aParticle.Energy/units.eV << "\t" << aParticle.Weight << "\t" + << aParticle.X[0]/T << "\t" << aParticle.X[1]/T << "\t" << 1.-aParticle.X[2]/T << "\t" + << aParticle.Pdir[0] << "\t" << aParticle.Pdir[1] << "\t" << 1.-aParticle.Pdir[2] << "\t" + << aParticle.fCascadeProductionTime.z() << "\t" << aParticle.Ninteractions; + if(fSaveSourceZ) + aOut << "\t" << aParticle.SourceParticle->Time.z(); + if(fSaveSourceE) + aOut << "\t" << aParticle.SourceParticle->Energy/units.eV; + if(fSaveId){ + aOut << "\t" << aParticle.id; + } + } + + void RawOutput3D::WriteHeader(std::ostream& aOut, const Particle& aFirstParticle){ + aOut << "# " << Particle::Name(aFirstParticle.Type); + if(!fSaveSourceZ){ + aOut << "\tz_src = "<< aFirstParticle.SourceParticle->Time.z(); + } + if(!fSaveSourceE){ + aOut << "\tE_src/eV = "<< aFirstParticle.SourceParticle->Energy/units.eV; + } + aOut << "\t" << "T/Mpc = " << (aFirstParticle.Time.t()-aFirstParticle.SourceParticle->Time.t())/units.Mpc << "\n#\n"; + aOut << "# E/eV\tweight\tx/T\ty/T\t1-(z/T)\tPx/P\tPy/P\t1-(Pz/P)\tz_cascade_production\tN_interactions"; + if(fSaveSourceZ){ + aOut << "\tz_src"; + } + if(fSaveSourceE){ + aOut << "\tE_src/eV"; + } + if(fSaveId){ + aOut << "\tId"; + } + } + +} /* namespace mcray */ diff --git a/src/lib/Output.h b/src/lib/Output.h new file mode 100644 index 0000000..851c024 --- /dev/null +++ b/src/lib/Output.h @@ -0,0 +1,108 @@ +/* + * Output.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#ifndef OUTPUT_H_ +#define OUTPUT_H_ + +#include "Particle.h" +#include <string> +#include "Utils.h" +#include <fstream> + +namespace mcray { + +class IOutput : public ISmartReferencedObj{ +public: + virtual void AddParticle(const Particle& aParticle) = 0; + virtual void Flush(bool aFinal) = 0; +}; + +class SpectrumOutput : public TSmartReferencedObj<IOutput> { +public: + SpectrumOutput(std::string aFile, double aEmin, double aStep); + virtual ~SpectrumOutput(); + void AddParticle(const Particle& aParticle); + void Flush(bool aFinal); +private: + Vector fSpectra[ParticleTypeEOF]; + std::vector<int> fNumberOfParticles[ParticleTypeEOF]; + double fEmin; + double fLogStep; + std::string fFile; +}; + +class RawOutput : public TSmartReferencedObj<IOutput> { +public: + RawOutput(std::string aDir, bool aCheckRepetitions, bool aOverwriteExisting = false); + virtual ~RawOutput(); + void AddParticle(const Particle& aParticle); + void Flush(bool aFinal); + void SetOutputDir(std::string aDir, bool aOverwriteExisting = false); + virtual void WriteHeader(std::ostream& aOut, const Particle& aFirstParticle); + virtual void Write(std::ostream& aOut, const Particle& aParticle); + + //TODO: delete this method when intermediate output is implemented via multiple IResult instances in PropagEngine + void CopyToStack(class ParticleStack& aStack); +private: + void LookForRepetitions(std::vector<Particle>* aParticles); + std::vector<Particle>* fParticles; + std::vector<bool> fHeaderWritten; + std::string fDir; + bool fCheckRepetitions; + bool fFlushInProgress; +}; + + class RawOutput3D : public RawOutput{ + public: + RawOutput3D(std::string aDir, bool aOverwriteExisting = false, bool aSaveSourceZ=false, bool aSaveSourceE=false, bool aSaveId=false): + RawOutput(aDir, false, aOverwriteExisting),fSaveSourceZ(aSaveSourceZ), fSaveSourceE(aSaveSourceE), fSaveId(aSaveId){ } + void WriteHeader(std::ostream& aOut, const Particle& aFirstParticle); + void Write(std::ostream& aOut, const Particle& aParticle); + private: + bool fSaveSourceZ; + bool fSaveSourceE; + bool fSaveId; + }; + + class TotalEnergyOutput : public TSmartReferencedObj<IOutput> { + public: + TotalEnergyOutput(){memset(fE,0,ParticleTypeEOF*sizeof(double));fTotE=0.;} + void AddParticle(const Particle& aParticle) { + fTotE += aParticle.Energy*aParticle.Weight; + fE[aParticle.Type] += aParticle.Energy*aParticle.Weight; + } + void Flush(bool aFinal){} + double TotalE() const { return fTotE; } + double TotalE(ParticleType aType) const { return fE[aType]; } + private: + double fTotE; + double fE[ParticleTypeEOF]; + }; + +} /* namespace mcray */ +#endif /* OUTPUT_H_ */ diff --git a/src/lib/PPP.cpp b/src/lib/PPP.cpp new file mode 100644 index 0000000..3d2d413 --- /dev/null +++ b/src/lib/PPP.cpp @@ -0,0 +1,485 @@ +/* + * PPP.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#include "PPP.h" +#include "MathUtils.h" +#include "Output.h" +#include "ParticleStack.h" +#include "TableBackgrounds.h" +#include "PropagationEngine.h" +#include "ProtonPP.h" +#include "Test.h" + +namespace Interactions { +using namespace mcray; +using namespace Utils; + +PPP::PPP(BackgroundIntegral* aBackground, double aScaleFactor): +fBackground(aBackground), +f_ScaleFactor(aScaleFactor), +fMp(Particle::Mass(Proton)), +fMe(Particle::Mass(Electron)) +{ + ASSERT(aScaleFactor>0); +} + +RandomInteraction* PPP::Clone() const +{ + return new PPP(fBackground->Clone(), f_ScaleFactor); +} + +PPP::~PPP() { +} + +PPP::SigmaTot::SigmaTot(): +fMp(Particle::Mass(Proton)), +fMe(Particle::Mass(Electron)), +fThresholdS(fMp*(4.*fMe+fMp)), +fMaxS(1e300),//1e2*fMp*fMp +fSigmaConst(AlphaEM*AlphaEM*AlphaEM/fMe/fMe)//alpha*r_0 +{ + +} + +double PPP::SigmaTot::f(double s) const +{ + double k = 0.5*(s/fMp-fMp)/fMe;//photon energy in proton rest frame in units of electron mass + if(k<=2) + return 0; + double result = 0.; + if(k<4) + {//formula (A1) from M. J. Chodorowski, A. A. Zdziarski, and M. Sikora, Astrophys. J. 400, 181 (1992) + double eta = (k-2.)/(k+2.); + result = (k-2.)/k; + result *= (result*result*2./3.*M_PI*(1.+eta*(0.5+eta*(23./40.+eta*(37./120.+eta*61./192.))))); + } + else + {//formula (A2) from M. J. Chodorowski, A. A. Zdziarski, and M. Sikora, Astrophys. J. 400, 181 (1992) + double ln2k = log(2.*k); + double _kk = 4./k/k; + double _kk2 = _kk*_kk; + result = 28./9.*ln2k-218./27.+_kk*(ln2k*(6.-M_PI*M_PI/3.+ln2k*(-1+2./3.*ln2k))+0.549054066848226); + result -= (_kk2*(0.1875*ln2k+0.125+_kk*(0.0125868055555556*ln2k-0.00557002314814815))); + } + result *= fSigmaConst; + ASSERT_VALID_NO(result); + return result; +} + +PPP::DifSigma_Eprime::DifSigma_Eprime(): +fE(-1), +fK(-1), +fCoef(-1) +{ + fKern_Theta.function = sigma; + fKern_Theta.params = this; +} + +double PPP::s_epsRel = 1e-4; + +double PPP::DifSigma_Eprime::f(double E) const +{ + ((PPP::DifSigma_Eprime*)this)->fE = E; + double result = ((Utils::MathUtils)fMath).Integration_qag(fKern_Theta,-1.,1.,0, s_epsRel, (size_t)(1./s_epsRel)); + ASSERT_VALID_NO(result); + return result*fCoef; +} + +void PPP::DifSigma_Eprime::SetK(double aK) +{ + fK = aK; + fCoef = 0.5*fSigmaConst/(aK*aK*aK); +} + + +double PPP::DifSigma_Eprime::sigma(double aCosTheta, void* data) +{ + const PPP::DifSigma_Eprime* sig = (const PPP::DifSigma_Eprime*)data; + return PPP::DiffSigma(sig->fK, sig->fE, aCosTheta); +} + +//d(sigma)/d(e)/d(eps) +double PPP::DiffSigma(double k, double em, double cosT) +{//formula (10) of Blumenthal, Phys Rev D (1970) vol 1, number 6, page 1596 +// all quantities are given in units of electron mass +// em - electron energy in proton rest frame +// k - photon energy in proton rest frame +// cosT - angle between electron and photon in proton rest frame +// constant multiplier alpha*r_0^2/2k^3 is omitted + if(em<=1.) + return 0.; + double em2 = em*em; + double ep = k-em;//positron energy in proton rest frame (we neglect proton recoiling momentum) + if(ep<=1.) + return 0.; + double ep2 = ep*ep; + double pm2 = em2-1.; + double pm = sqrt(pm2);//electron momentum in proton rest frame + double pp = sqrt(ep2-1.);//positron momentum in proton rest frame + double e=em-cosT*pm;//delta_ - electron energy in the LAB (related to observer on Earth) frame divided by proton gamma factor + double e2 = e*e; + double e4 = e2*e2; + double k2 = k*k; + double sin2T = 1.-cosT*cosT; + double T2 = k2+pm2-2.*k*pm*cosT; + double T = sqrt(T2); + double Y = 2./pm2*log((ep*em+pp*pm+1)/k); + double ep_pp = (ep-pp<1e-8)? 0.5/ep/ep : (ep-pp); + double yp = log((ep+pp)/ep_pp)/pp; + double deltaTp = log((T+pp)/(T-pp)); + double result = -4.*sin2T*(2.*em2+1.)/(pm2*e4) + (5.*em2-2.*ep*em+3.)/(pm2*e2) + (pm2-k2)/(T2*e2) + 2.*ep/(pm2*e); + result += (Y/(pm*pp)*(2.*em*sin2T*(3.*k+pm2*ep)/e4 + (2.*em2*(em2+ep2)-7.*em2-3.*ep*em-ep2+1.)/e2 + k*(em2-em*ep-1.)/e)); + result -= (deltaTp/(pp*T)*(2./e2-3.*k/e-k*(pm2-k2)/(T2*e)) + 2.*yp/e); + result *= (pp*pm); + if(result<0) + return 0.; + ASSERT_VALID_NO(result); + return result; +} + +//d(sigma)/d(e)/d(eps) +double PPP::DifSigma_r::Kern::f(double eps) const +{//formula (10) of Blumenthal, Phys Rev D (1970) vol 1, number 6, page 1596 + // all quantities are given in units of electron mass + // eps = E_ - electron energy in proton rest frame + // k - photon energy in proton rest frame + // e=delta_ - electron energy in the LAB (related to observer on Earth) frame divided by proton gamma factor + // cos(Theta)=(E_-e)/p_; constant multiplier alpha*r_0^2/2k^3 is omitted + // and extra multiplier dcosTheta/de = 1/p_ is introduced to take into account change of integration variables cosTheta -> e + double em = eps;//electron energy in proton rest frame + ASSERT(em>1); + double em2 = em*em; + double ep = k-em;//positron energy in proton rest frame (we neglect proton recoiling momentum) + ASSERT(ep>1); + double ep2 = ep*ep; + double pm2 = em2-1.; + double pm = sqrt(pm2);//electron momentum in proton rest frame + double pp = sqrt(ep2-1.);//positron momentum in proton rest frame + double cosT = (em-e)/pm; //cos(Theta) the angle between photon and electron in p-rest frame + double sin2T = 1.-cosT*cosT; + if(cosT>=1.) + { + sin2T = (2.*e*pm-1.)/pm2; + if(sin2T<0) + return 0; + ASSERT(sin2T<1e-3); + cosT = 1.-0.5*sin2T; + } + double T2 = k2+pm2-2.*k*pm*cosT; + double T = sqrt(T2); + double Y = 2./pm2*log((ep*em+pp*pm+1)/k); + double ep_pp = (ep-pp<1e-8)? 0.5/ep/ep : (ep-pp); + double yp = log((ep+pp)/ep_pp)/pp; + double deltaTp = log((T+pp)/(T-pp)); + double result = -4.*sin2T*(2.*em2+1.)/(pm2*e4) + (5.*em2-2.*ep*em+3.)/(pm2*e2) + (pm2-k2)/(T2*e2) + 2.*ep/(pm2*e); + result += (Y/(pm*pp)*(2.*em*sin2T*(3.*k+pm2*ep)/e4 + (2.*em2*(em2+ep2)-7.*em2-3.*ep*em-ep2+1.)/e2 + k*(em2-em*ep-1.)/e)); + result -= (deltaTp/(pp*T)*(2./e2-3.*k/e-k*(pm2-k2)/(T2*e)) + 2.*yp/e); + result *= pp; + if(result<0) + return 0.;//TODO find the reason + //ASSERT_VALID_NO(result); + return result; +} + +void PPP::DifSigma_r::Kern::SetParams(double _k, double _e) +{ + k = _k; + k2 = k*k; + e = _e;//electron energy in lab frame divided by proton gamma factor + e2 = e*e; + e4 = e2*e2; + fEpsMin = 0.5*(e + 1./e); + //ASSERT(Xmin()<=Xmax());//if Xmin>Xmax PPP::DifSigma::f(double r) will return 0 + //(this may occure for large k (k>m_p, since approximation of zero proton kinetic energy is not valid anymore) +} + +void PPP::DifSigma_r::SetS(double aS) +{ + fK = 0.5*(aS/fMp-fMp)/fMe;//photon energy in proton rest frame in units of electron mass + double m = fMp/fMe;//proton mass in units of electron mass + double tmp = fK + m - 1.; + tmp *= tmp; + //maximal momentum of electron directed opposite to photon momentum + //in proton rest frame in units of electron mass + double maxP = (sqrt(fK*(fK*(-1 + m) - 2*m)*(-1 + m)*tmp) + + fK*(-1 + fK + m - fK*m))/((-1 + m)*(-1 + 2*fK + m)); + f_rMax = (sqrt(maxP*maxP+1.)+maxP)/m;//converting to lab frame and dividing by proton energy + ASSERT(f_rMax<1); + //maximal momentum of electron directed along photon momentum + //in proton rest frame in units of electron mass + double minP = ((-1 + fK)*fK*(-1 + m) + sqrt(fK*(fK*(-1 + m) - 2*m)*(-1 + m)* + tmp))/((-1 + m)*(-1 + 2*fK + m)); + + //converting to lab frame and dividing by proton energy + f_rMin = (minP<1e4 ? (sqrt(minP*minP+1.)-minP) : 0.5/minP/minP)/m; +} + +double PPP::DifSigma_r::f(double r) const +{ + double e = fMp/fMe*r;// E_e/gamma_p in units of electron mass + Kern fKern; + fKern.SetParams(fK, e); + double epsMin = fKern.Xmin(); + double epsMax = fKern.Xmax(); + double relDiff = (epsMax-epsMin)/epsMax; + if(relDiff<1e-9) + return 0.; + const size_t limit = (size_t)(1./s_epsRel); + double relError = ( relDiff > 1e-5)? s_epsRel : (relDiff<3e-7?0.1:0.01); + double result = ((MathUtils)fMath).Integration_qag((gsl_function)fKern, epsMin, epsMax, 0, relError, limit); + ASSERT_VALID_NO(result); + result *= (0.5*fSigmaConst*fMp/fMe/fK/fK/fK); + return result; +} + +double PPP::Rate(const Particle& aParticle) const +{ + if(aParticle.Type != Proton) + return 0.; + return fBackground->GetRateS(fSigma, aParticle) / f_ScaleFactor; +} + +bool PPP::SampleS(const Particle& aParticle, double& aS, Randomizer& aRandomizer) const +{ + ASSERT(aParticle.Type == Proton); + aS = 0.; + return (fBackground->GetRateAndSampleS(fSigma, aParticle, aRandomizer, aS)!=0); +} + +void PPP::SampleSecondariesMean(Particle& aParticle, std::vector<Particle>& aSecondaries, double aS, Randomizer& aRandomizer) const +{//// sampling secondary electrons/positrons is expensive so thinning is performed before sampling + /// via usage of scale factor parameter and generating either electron or positron (not both) + ASSERT(aParticle.Type == Proton); + + Particle sec = aParticle; + sec.Type = aRandomizer.Rand()>0.5 ? Electron : Positron; + DifSigma_r difSigma; + double r = difSigma.SampleR(aS, s_epsRel, math, aRandomizer.Rand()); + sec.Energy *= r; + sec.Weight *= (f_ScaleFactor*2);//2 takes into account that pair is produced + sec.fCascadeProductionTime = aParticle.Time; + aSecondaries.push_back(sec); + + //don't add proton to the list of secondaries since this.KillsPrimary == false +} + + double PPP::DifSigma_r::SampleR(double aS, double aEpsRel, const Utils::MathUtils& aMath, double aRand) + { + double r = 0.; + SetS(aS); + if(fK<20000) + { + double sigmaTot; + aMath.SampleLogDistribution(*this, aRand, r, sigmaTot, Xmin(), Xmax(), aEpsRel); + ASSERT(r>=f_rMin && r<=f_rMax); + } + else + { //to avoid rounding error using analytical estimate dSigma/dr ~ r^{-7/4} for fK>>1 + // < r > = m_e/m_p //this is clear from plotting r^2 dSigma/dr in log scale (try e.g. PPP::UnitTest) + double rMinAdj = f_rMin; + for(int nIt=1; true; nIt++)//approximately solving eq. + { + double rMinNew = pow(3.*fMp/fMe*(pow(f_rMax,0.25)-pow(rMinAdj,0.25))+pow(f_rMax,-0.75),-4./3.); + ASSERT(rMinNew>0 && rMinNew<f_rMax); + double relErr = 2.*fabs(rMinNew-rMinAdj)/(rMinNew+rMinAdj); + rMinAdj = rMinNew; + if(relErr<aEpsRel) + break; + ASSERT(nIt<50); + } + //double rMaxAdj = fMe/fMp/3./pow(f_rMin,0.75) + pow(f_rMin,0.25); //adjust rMin to have < r > = m_e/m_p (assuming rMin<<rMax) + //ASSERT(rMaxAdj<1.); + //ASSERT(rMaxAdj/f_rMin<1e-3); + r = pow(pow(rMinAdj,-0.75)*(1.-aRand)+aRand*pow(f_rMax,-0.75) , -4./3.); + ASSERT(r>=rMinAdj && r<=f_rMax); + } + return r; + } + + +void PPP::SampleSecondariesExact(Particle& aParticle, std::vector<Particle>& aSecondaries, double aS, Randomizer& aRandomizer) const +{ + ASSERT(aParticle.Type == Proton); + double m = aParticle.Mass(); + double k = 0.5*(aS/m-m)/fMe;//photon energy in proton rest frame in units of electron mass + ASSERT(k>2); + ((PPP::DifSigma_Eprime&)fDifSigma_Eprime).SetK(k); + + double Eprime, cosTheta, sigmaTot; + ((MathUtils&)math).SampleLogDistribution(fDifSigma_Eprime, aRandomizer.Rand(), Eprime, sigmaTot, fDifSigma_Eprime.Xmin(), fDifSigma_Eprime.Xmax(), 10.*s_epsRel); + CosThetaSamplingEq cosProb(Eprime, k); + ((MathUtils&)math).SampleDistribution(cosProb, aRandomizer.Rand(), cosTheta, sigmaTot, cosProb.Xmin(), cosProb.Xmax(), s_epsRel); + Particle electron = aParticle; + electron.Type = Electron; + electron.Energy *= ((Eprime-cosTheta*sqrt(Eprime-1.))*fMe/m); + Particle positron = aParticle; + positron.Type = Positron; + Eprime = k-Eprime;//we neglect proton kinetic energy + positron.Energy *= ((Eprime+cosTheta*sqrt(Eprime-1.))*fMe/m);//we assume that positron is directed against electron??? TODO: this is probably wrong + aParticle.Energy -= (electron.Energy + positron.Energy); + if(aParticle.Energy < m) + Exception::Throw("PPP::SampleSecondariesExact error: Ep<m"); + aParticle.fCascadeProductionTime = aParticle.Time; + electron.Weight *= f_ScaleFactor; + positron.Weight *= f_ScaleFactor; + aSecondaries.push_back(electron); + aSecondaries.push_back(positron); +} + +void PPP::SampleSecondaries(Particle& aParticle, std::vector<Particle>& aSecondaries, double aS, Randomizer& aRandomizer) const +{ + //SampleSecondariesExact(aParticle, aSecondaries, aS, aRandomizer); //wrong calculation (inelasticity growing with S instead of dropping) + //also in SampleSecondariesExact we assume that positron is directed against electron which is probably wrong + + //this should to be correct on the average although not precise for individual interactions + //since electron and positron energies are sampled independently of each other for individual interactions + SampleSecondariesMean(aParticle, aSecondaries, aS, aRandomizer); +} + +double PPP::CosThetaSamplingEq::f(double aCosT) const +{ + return PPP::DiffSigma(fK,fE,aCosT); +} + +void PPP::UnitTest() +{ + Utils::MathUtils math; + SigmaTot sigma; + DifSigma_r difSigma; + double Mp(Particle::Mass(Proton)); + double Me(Particle::Mass(Electron)); + std::ofstream sigmaPPout; + sigmaPPout.open("ppp_diff_sigma",std::ios::out); + std::cout << "k/m_e\tsigTot/mbarn\t2.*(sigTot-sigIntTot)/(sigTot+sigIntTot)" << std::endl; + for(double k=2.2; k<3e4; k*=10.) + { + double s = Mp + k*Me; + s*=s; + //if(s>sigma.Xmax()) + // continue; + double sigTot = sigma(s); + difSigma.SetS(s); + double sigIntTot = math.Integration_qag((gsl_function)difSigma, difSigma.Xmin(), difSigma.Xmax(), 0, 1e-5, 10000); + std::cout << k << "\t" << sigTot/units.barn*1e3 << "\t" << 2.*(sigTot-sigIntTot)/(sigTot+sigIntTot) << std::endl; + sigmaPPout << "# k=" << k << std::endl; + difSigma.Print(sigmaPPout,100,true); + sigmaPPout << "\n" << std::endl; + } + UnitTestSampling(1e12, 0); + UnitTestEconserv(1e20, 0.1); +} + +void PPP::UnitTestSampling(double aE, double aZ) +{ + std::string out = "debug_sample_ppp_E"; + out += ToString(aE*1e6);//eV + out += "_Z"; + out += ToString(aZ); + std::ofstream secPPout; + secPPout.open("sample_sec_ppp",std::ios::out); + //std::ofstream secPPoutVarS; + //secPPoutVarS.open("sample_sec_ppp_varS",std::ios::out); + + debug.SetOutputFile(out); + + if(!cosmology.IsInitialized()) + cosmology.Init(); + + CompoundBackground backgr; + double cmbTemp = 2.73/Units::phTemperature_mult/units.Eunit; + + backgr.AddComponent(new PlankBackground(cmbTemp, 1e-3*cmbTemp, 1e3*cmbTemp)); + //backgr.AddComponent(new PlankBackground(cmbTemp, 6.2e-10/units.Eunit, 6.4e-10/units.Eunit));//only central value + + double stepZ = 0.05; + double logStepK = pow(10,0.05); + double epsRel = 1e-3; + + Backgrounds::Kneiske0309EBL* kn0309 = new Backgrounds::Kneiske0309EBL(); + + backgr.AddComponent(kn0309); + + PBackgroundIntegral backgrI = new ContinuousBackgroundIntegral(backgr, stepZ, logStepK, aZ+1, epsRel); + Interactions::PPP pp(backgrI); + Particle proton(Proton, aZ); + proton.Energy = aE;//MeV + proton.Time.setZ(aZ); + proton.Type = Proton; + Randomizer rnd; + /* + double S=2.; + for(int i=0; i<10000; i++) + { + std::vector<Particle> secondaries; + if(pp.GetSecondaries(proton, secondaries, rnd))//this outputs sampled s to debug file + { std::vector<Particle> secondariesS; + pp.SampleSecondaries(proton, secondariesS, S rnd); + ASSERT(secondaries.size()==3); + //print electron and proton energies + secPPoutVarS << secondaries[0].Energy/aE << "\n" << secondaries[1].Energy/aE << "\n"; + } + }*/ + + double Mp(Particle::Mass(Proton)); + double Me(Particle::Mass(Electron)); + SigmaTot sigma; + for(double k=2.2; k<3e5; k*=10.) + { + double s = Mp + k*Me; + s*=s; + if(s>sigma.Xmax()) + continue; + for(int i=0; i<100; i++) + { + std::vector<Particle> secondaries; + pp.SampleSecondaries(proton, secondaries, s, rnd); + ASSERT(secondaries.size()==1);//only electron or positron is sampled + secPPout << k << "\t" << secondaries[0].Energy/aE << "\n"; + //std::cout << k << "\t" << secondaries[0].Energy/aE << "\n"; + } + secPPout << "\n" << std::endl; + //std::cout << std::endl; + } + + secPPout.close(); +} + +void PPP::UnitTestEconserv(double aE, double aZ){ + uint scaleFactorPPP = 10; + int nParticles = 100; + Test::EConservationTest test(aZ,1e6,2015); + test.alphaThinning = 0.9;//alpha = 1 conserves number of particles on the average; alpha = 0 disables thinning + test.Engine().AddInteraction(new PPP(test.Backgr(),scaleFactorPPP)); + test.Engine().AddInteraction(new ProtonPPcel(test.Backgr())); + Particle p(Proton, aZ); + p.Energy = aE*units.eV; + test.SetPrimary(p, nParticles); + test.Run(); +} + +} /* namespace Interactions */ diff --git a/src/lib/PPP.h b/src/lib/PPP.h new file mode 100644 index 0000000..d7320df --- /dev/null +++ b/src/lib/PPP.h @@ -0,0 +1,149 @@ +/* + * PPP.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef PPP_H_ +#define PPP_H_ + +#include "GammaPP.h" +#include "Interaction.h" + +namespace Interactions { +using namespace mcray; + +/// This class samples secondaries from pair production by protons +/// The energy loss by protons due to pair production is implemented +/// by class ProtonPPcel +class PPP: public RandomInteractionS, public EMInteraction { + class SigmaTot : public Function + { + public: + SigmaTot(); + double f(double s) const; + double Xmin() const { return fThresholdS; } + double Xmax() const { return fMaxS; } + protected: + const double fMp; + const double fMe; + const double fThresholdS; + const double fMaxS;//introduced to avoid computational problems (with differential cross section) + //for extremely large S (sqrt(S)>10 m_p, where PPP is much less important than pion production anyway) + const double fSigmaConst; + }; + class DifSigma_r : public SigmaTot + { + class Kern : public Function + { + public: + double f(double eps) const; + void SetParams(double _k, double _e); + double Xmin() const { return fEpsMin; } + double Xmax() const { return k - 1.; } + private: + double fEpsMin; + double k; + double k2;//k^2 + double e; + double e2; + double e4; + }; + public: + double f(double r) const; + void SetS(double aS); + double SampleR(double aS, double aEpsRel, const Utils::MathUtils& aMath, double aRand); + double Xmin() const { return f_rMin; } + double Xmax() const { return f_rMax; } + private: + //Kern fKern; + double fK;//photon energy in proton rest frame in units of electron mass + double f_rMin; + double f_rMax; + Utils::MathUtils fMath; + }; + + class DifSigma_Eprime : public SigmaTot + {// dSigma/dE_, where E_ electron gamma factor in proton rest frame + public: + DifSigma_Eprime(); + void SetK(double aK);//k-photon energy in proton rest frame in units of electron mass + double f(double E) const; + static double sigma(double aCosTheta, void* data); + double Xmin() const { return 1.; } + double Xmax() const { return fK-1.; } + private: + double fE;//electron gamma factor in proton rest frame + double fK;//photon energy in proton rest frame in units of electron mass + gsl_function fKern_Theta; + Utils::MathUtils fMath; + double fCoef; + }; + + class CosThetaSamplingEq : public Function + { + double fE; + double fK; + public: + CosThetaSamplingEq(double aEprime, double aK):fE(aEprime),fK(aK){} + double Xmin() const { return -1.; } + double Xmax() const { return 1.; } + double f(double aCosT) const; + }; + +public: + PPP(BackgroundIntegral* aBackground, double aScaleFactor = 1.); + static void UnitTest(); + static void UnitTestSampling(double aE, double aZ); + static void UnitTestEconserv(double aE, double aZ); + static double DiffSigma(double aK, double aE, double aCosTheta); + virtual ~PPP(); + RandomInteraction* Clone() const; + virtual bool KillsPrimary() const + { + return false; + } + double Rate(const Particle& aParticle) const; + bool SampleS(const Particle& aParticle, double& aS, Randomizer& aRandomizer) const; + void SampleSecondaries(Particle& aParticle, std::vector<Particle>& aSecondaries, double aS, Randomizer& aRandomizer) const; + void SampleSecondariesMean(Particle& aParticle, std::vector<Particle>& aSecondaries, double aS, Randomizer& aRandomizer) const; + void SampleSecondariesExact(Particle& aParticle, std::vector<Particle>& aSecondaries, double aS, Randomizer& aRandomizer) const; + + +private: + SmartPtr<BackgroundIntegral> fBackground; + SigmaTot fSigma; + DifSigma_r fDifSigma; + DifSigma_Eprime fDifSigma_Eprime; + double f_ScaleFactor;//>=0 divide interaction rate by f_ScaleFactor and + // multiply weight of secondary electrons and positrons by f_ScaleFactor + double fMp; + double fMe; + Utils::MathUtils math; + static double s_epsRel;//Desired relative error of integration +}; + +} /* namespace Interactions */ + +#endif /* PPP_H_ */ diff --git a/src/lib/Particle.cpp b/src/lib/Particle.cpp new file mode 100644 index 0000000..62f28a0 --- /dev/null +++ b/src/lib/Particle.cpp @@ -0,0 +1,195 @@ +/* + * Particle.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#include "Particle.h" +#include "Nucleus.h" + +namespace mcray +{ + +const double Particle::MassesMeV[EndLightParticle] = +{// order of masses in this array must follow the order of particles in the enumeration ParticleType +//#ifdef ELMAG_TEST + 0.511, //Electron, + 0.511,//Positron, +//#else + //0.510998928, //Electron, + //0.510998928,//Positron, +//#endif + 0,//Photon, + 0,//NeutrinoE, + 0,//NeutrinoM, + 0,//NeutrinoT, + 0,//NeutrinoAE, + 0,//NeutrinoAM, + 0,//NeutrinoAT, + 939.565378,//Neutron, + 938.272046//Proton, + +}; + +const int Particle::ElectricCharges[EndLightParticle] = +{// order of masses in this array must follow the order of particles in the enumeration ParticleType + -1, //Electron, + 1,//Positron, + 0,//Photon, + 0,//NeutrinoE, + 0,//NeutrinoM, + 0,//NeutrinoT, + 0,//NeutrinoAE, + 0,//NeutrinoAM, + 0,//NeutrinoAT, + 0,//Neutron, + 1//Proton, +}; + +const char* Particle::ParticleNames[ParticleTypeEOF] = +{ + "electron", + "positron", + "photon", + "neutrinoE", + "neutrinoM", + "neutrinoT", + "neutrinoAE", + "neutrinoAM", + "neutrinoAT", + "neutron", + "proton", + "A2", + "A3", + "A4", + "A5", + "A6", + "A7", + "A8", + "A9", + "A10", + "A11", + "A12", + "A13", + "A14", + "A15", + "A16", + "A17", + "A18", + "A19", + "A20", + "A21", + "A22", + "A23", + "A24", + "A25", + "A26", + "A27", + "A28", + "A29", + "A30", + "A31", + "A32", + "A33", + "A34", + "A35", + "A36", + "A37", + "A38", + "A39", + "A40", + "A41", + "A42", + "A43", + "A44", + "A45", + "A46", + "A47", + "A48", + "A49", + "A50", + "A51", + "A52", + "A53", + "A54", + "A55", + "A56" +}; + + int Particle::fNextCustomDataIndex = 0; + unsigned long long Particle::fMaxId = 0; + + void Particle::PropagateFreely(cosmo_time dt) + { + Time += dt; + double b=beta(); + for(int i=0; i<3; i++) + X[i]+=(b*Pdir[i]*dt); + } + + void Particle::GenerateId(){ +#pragma omp critical (Particle) + id = ++fMaxId; + } + + int Particle::ReserveInteractionDataSlot()//returns index to access and store interaction data or -1 if no space left + { + int result = -1; +#pragma omp critical (Particle) + { + if(fNextCustomDataIndex < ParticleCustomDataSize) + result = fNextCustomDataIndex; + fNextCustomDataIndex++; + } + return result; + } + + std::string Particle::ToString(){ + std::ostringstream logStr; + logStr << ParticleNames[Type] << "(E=" << Energy/units.eV << "eV, " << Time.ToString() << ")"; + return logStr.str(); + } + + double Particle::Mass(ParticleType aType){ + if(aType<EndLightParticle) + return MassesMeV[aType]/units.Eunit; + if(aType<EndNuclei){ + double meanNucleonMassMeV = 0.5*(Particle::Mass(Proton)+Particle::Mass(Neutron)); + return meanNucleonMassMeV*Interactions::CNucleus::getA(aType); + } + NOT_IMPLEMENTED // treat other particles when added + return 0.; + } + + double Particle::ElectricCharge(ParticleType aType){ + if(aType<EndLightParticle) + return ElectricCharges[aType]; + if(aType<EndNuclei) + return ElectricCharges[Proton]*Interactions::CNucleus::getZ(aType); + NOT_IMPLEMENTED // treat other particles when added + return 0.; + } + +} diff --git a/src/lib/Particle.h b/src/lib/Particle.h new file mode 100644 index 0000000..54a5700 --- /dev/null +++ b/src/lib/Particle.h @@ -0,0 +1,223 @@ +/* + * Particle.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef PARTICLE_H +#define PARTICLE_H + +#include "Cosmology.h" +#include <cstring> +#include "Units.h" +#include <math.h> + +namespace mcray +{ + enum ParticleType + {// order of masses in the array Particle::massesMeV must follow the order of particles below + Electron = 0, + Positron, + Photon, + NeutrinoE, + NeutrinoM, + NeutrinoT, + NeutrinoAE, + NeutrinoAM, + NeutrinoAT, + Neutron, + Proton, + EndLightParticle, + StartNuclei = EndLightParticle, + A02 = StartNuclei, + A03, + A04, + A05, + A06, + A07, + A08, + A09, + A10, + A11, + A12, + A13, + A14, + A15, + A16, + A17, + A18, + A19, + A20, + A21, + A22, + A23, + A24, + A25, + A26, + A27, + A28, + A29, + A30, + A31, + A32, + A33, + A34, + A35, + A36, + A37, + A38, + A39, + A40, + A41, + A42, + A43, + A44, + A45, + A46, + A47, + A48, + A49, + A50, + A51, + A52, + A53, + A54, + A55, + A56, + EndNuclei, + ParticleTypeEOF = EndNuclei //must be the last + }; +#define ParticleCustomDataSize 16 + class Particle + { + private: + inline void Reset() { + memset(this,0,sizeof(Particle));Weight=1.; LastB = -1.; Pdir[2]=1.; + } + void GenerateId(); + public: + inline double Mass() const { + return Mass(Type); + } + inline int ElectricCharge() const { + return ElectricCharge(Type); + } + //Construct primary particle + inline Particle(ParticleType aType, cosmo_time aZsource){ + Reset(); Type=aType; fProductionTime.setZ(aZsource) ; Time.setZ(aZsource); SourceParticle = this; + GenerateId(); + } + + inline Particle& operator=(const Particle& aParticle) { + memcpy(this, &aParticle, sizeof(Particle)); return *this; + } + inline double beta() const { + double m=Mass(); return sqrt(1.-m*m/Energy/Energy); + } + + inline void SetParent(const Particle& aParticle){ + GenerateId(); + if(id != aParticle.id){ + fPrevId = aParticle.id; + fProductionTime = Time; + } + } + + static double Mass(ParticleType aType); + static double ElectricCharge(ParticleType aType); + void PropagateFreely(cosmo_time dt); + ParticleType Type; + double Weight; + CosmoTime Time; + double Energy; + long Ninteractions;//number of interactions in the interaction chain + double X[3];//comoving coordinates, source location: (0,0,0) + double Pdir[3];//momentum direction, initial value: (0,0,1) + unsigned long long id; + unsigned long long fPrevId; + + // TODO: interaction specific attributes should be stored separately in custom structures which can + // be accessed via pointers stored in interactionData field + void* interactionData[ParticleCustomDataSize];//used to store interaction specific attributes as pointers + static int ReserveInteractionDataSlot();//returns index to access and store interaction data or -1 if no space left + + const Particle* SourceParticle; + double Deflection2;//uncorrelated deflection angle squared + double CorrelatedDeflection;//current value of correlated deflection + mutable double CorrelatedBpath;// travel distance within B-field coherence length + CosmoTime fProductionTime; // time when the particle was produced either in source or on the way as secondary from interaction; + CosmoTime fCascadeProductionTime; // time when primary EM cascade particle was produced by hadron + double dt; // particle time delay; + mutable double LastB; //last transverce magnetic field within B-field coherence length + + inline double JetOpenningAngle() const + { + return DeflectionAngle()-ObservationAngle(); + } + + inline CosmoTime LastDeflectionTime() const + { + return ElectricCharge() ? Time : fProductionTime; + } + + inline double ObservationAngle() const + { + double beta = DeflectionAngle(); + if(beta==0.) + return 0.; + double sinBeta=sin(beta); + double pathToLastDeflection = LastDeflectionTime().t()-SourceParticle->Time.t();//distance from the source for the parent electron/positron (valid for small z source and deflection) + double distanceToSource = Time.t()-SourceParticle->Time.t();// distance between the source and the observer (valid for small z source) + return asin(pathToLastDeflection/distanceToSource*sinBeta);// observation angle + } + + inline double DeflectionAngle() const + { + return sqrt(Deflection2 + CorrelatedDeflection*CorrelatedDeflection); + } + + inline double TimeDelay() const + { + if(ElectricCharge()) + return 0.;//the approximation used below works only for neutral particles ( LastDeflectionTime() > Time.t() ) + double sinBeta=sin(DeflectionAngle()); + double pathToLastDeflection = LastDeflectionTime().t()-SourceParticle->Time.t();//xx - distance from the source for the parent electron/positron (valid for small z source and deflection) + double distanceToSource = Time.t()-SourceParticle->Time.t();// distance between the source and the observer (valid for small z source) + return 2*pathToLastDeflection*(1.0-pathToLastDeflection/distanceToSource)*sinBeta*sinBeta;// time delay + } + inline static const char* Name(ParticleType aType) + { + return ParticleNames[aType]; + } + std::string ToString(); + private: + static const double MassesMeV[EndLightParticle]; + static const int ElectricCharges[EndLightParticle]; + static const char* ParticleNames[ParticleTypeEOF]; + static int fNextCustomDataIndex; + static unsigned long long fMaxId; + }; +} +#endif /* PARTICLE_H */ + diff --git a/src/lib/ParticleStack.cpp b/src/lib/ParticleStack.cpp new file mode 100644 index 0000000..7871d7e --- /dev/null +++ b/src/lib/ParticleStack.cpp @@ -0,0 +1,210 @@ +/* + * ParticleStack.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "ParticleStack.h" +#include <signal.h> +#include "Utils.h" +#include <time.h> +#include <algorithm> + +namespace mcray +{ + +ParticleStack::ParticleStack(int aDebugOutputPeriod):fLastDebugOutputTime(0),fDebugOutputPeriod(aDebugOutputPeriod) { +} + +ParticleStack::~ParticleStack() { + for(std::vector<Particle*>::const_iterator it = fPrimaryParticles.begin(); it != fPrimaryParticles.end(); it++) + delete (*it); + fPrimaryParticles.clear(); +} + + +void ParticleStack::AddSecondaries(const std::vector<Particle> &aParticles) +{ +#pragma omp critical (ParticleStack) + { + for(std::vector<Particle>::const_iterator it = aParticles.begin(); it != aParticles.end(); it++) + fParticles.push_back(*it); +//#ifdef _DEBUG + time_t t = time(0); + if(fDebugOutputPeriod>=0 && t>fLastDebugOutputTime + fDebugOutputPeriod) + { + std::cerr << fOutputPrefix << "particle stack: " << fParticles.size() << std::endl; + fLastDebugOutputTime = t; + } +//#endif + } +} + +void ParticleStack::AddPrimary(const Particle &aParticle) +{ +#pragma omp critical (ParticleStack) + { + Particle* primary = new Particle(aParticle); + primary->SourceParticle = 0; + fPrimaryParticles.push_back(primary); + fParticles.push_back(aParticle); + //make sure SourceParticle field is valid until ParticleStack object is destroyed + fParticles.back().SourceParticle = primary; +//#ifdef _DEBUG + time_t t = time(0); + if(fDebugOutputPeriod>=0 && t>fLastDebugOutputTime + fDebugOutputPeriod) + { + std::cerr << fOutputPrefix << "particle stack: " << fParticles.size() << std::endl; + fLastDebugOutputTime = t; + } +//#endif + } +} + +bool ParticleStack::Pop(Particle& aParticle) +{ + bool result = false; +#pragma omp critical (ParticleStack) + { + if(fParticles.size()) + { + aParticle = fParticles.back();//*(fParticles.end()-1); + fParticles.pop_back();//fParticles.erase(fParticles.end()-1); +//#ifdef _DEBUG + time_t t = time(0); + if(fDebugOutputPeriod>=0 && t>fLastDebugOutputTime + fDebugOutputPeriod) + { + std::cerr << fOutputPrefix << "particle stack: " << fParticles.size() << std::endl; + fLastDebugOutputTime = t; + } +//#endif + result = true; + } + } + return result; +} + +void Result::Add(std::vector<Particle> aParticles) +{ + if(fFilter)///! filtering outside critical section + for(int i=aParticles.size()-1; i>=0; i--) + if (aParticles[i].Time < fT || (fFilter && !fFilter->Pass(aParticles[i]))) + aParticles.erase(aParticles.begin()+i); + +#pragma omp critical (Result_Add) + { + for(std::vector<Particle>::const_iterator pit = aParticles.begin(); pit != aParticles.end(); pit++) { + for (std::vector<SmartPtr<IOutput> >::iterator it = fOutputs.begin(); it != fOutputs.end(); it++) { + (*it)->AddParticle(*pit); + } + } + fParticleCounter++;//we only count number of Result::Add calls which should correspond to number of primary particles successfully treated + if(fAutoFlushInterval>0 && fParticleCounter>=fAutoFlushInterval){//flush only when all particles from current cascade are added + //to insure output is always selfconsistent + fParticleCounter = 0; + Flush(false); + } + } +} + +void Result::Add(const Particle& aParticle) +{ + if(aParticle.Time < fT || (fFilter && !fFilter->Pass(aParticle)))///! filtering outside critical section + return; +#pragma omp critical (Result_Add) + { + for(std::vector<SmartPtr<IOutput> >::iterator it = fOutputs.begin(); it != fOutputs.end(); it++) + { + (*it)->AddParticle(aParticle); + } + fParticleCounter++; + if(fAutoFlushInterval>0 && fParticleCounter>=fAutoFlushInterval){ + fParticleCounter = 0; + Flush(false); + } + } +} + +void Result::SetAutoFlushInterval(uint aNumberOfPrimaryParticles){ +#pragma omp critical (Result_Add) + { + fAutoFlushInterval = aNumberOfPrimaryParticles; + } +} + +void Result::Flush(bool aFinal) +{ +#pragma omp critical (Result_Flush) + { + for (std::vector<SmartPtr<IOutput> >::iterator it = fOutputs.begin(); it != fOutputs.end(); it++) { + (*it)->Flush(aFinal); + } + } +} + +void Result::SetEndTime(const CosmoTime& aT) +{ + fT = aT; +} + +cosmo_time Result::GetMinTravelTimeLeft(const Particle& aParticle) const +{ + return fT-aParticle.Time; +} + +Result::~Result() +{ + Flush(); + fOutputs.clear(); + + std::vector<Result *>::iterator pos = std::find(fCtrlCFlushList.begin(), fCtrlCFlushList.end(), this); + if (pos != fCtrlCFlushList.end()) + fCtrlCFlushList.erase(pos); + +} + +std::vector<Result*> Result::fCtrlCFlushList; + +void Result::EnableFlushOnCtrlC() { + if (!fCtrlCFlushList.size()) { + struct sigaction act; + act.sa_handler = Result::SIGINT_handler; + sigaction(SIGINT, &act, NULL);//register Ctrl-C handler + } + fCtrlCFlushList.push_back(this); +} + +void Result::SIGINT_handler(int) { +#pragma omp critical (Result_Add) + { + for (std::vector<Result *>::iterator it = fCtrlCFlushList.begin(); it != fCtrlCFlushList.end(); it++) { + (*it)->fAutoFlushInterval = 0;//disable autoflush + (*it)->Flush(true); + (*it)->fStopRequested = true; + } + } +} + +}//namespace mcray + diff --git a/src/lib/ParticleStack.h b/src/lib/ParticleStack.h new file mode 100644 index 0000000..d393896 --- /dev/null +++ b/src/lib/ParticleStack.h @@ -0,0 +1,122 @@ +/* + * ParticleStack.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#ifndef PARTICLESTACK_H +#define PARTICLESTACK_H + +#include "Particle.h" +#include "Output.h" +#include "Utils.h" +#include "Filter.h" +#include <vector> +#include <set> + +namespace mcray +{ + +//Thread-safe stack +class ParticleStack { +public: + ParticleStack(int aDebugOutputPeriod=-1); + //TODO: remove this method and RawOutput::CopyToStack() when intermediate output is implemented via multiple IResult instances in PropagEngine + //Currently used in OmegaCascade application + void AddSecondaries(const std::vector<Particle> &aParticles); + //void AddSecondary(const Particle &aParticle); + void AddPrimary(const Particle &aParticle); + bool Pop(Particle& aParticle); + virtual ~ParticleStack(); + inline void SetOutputPrefix(std::string aPrefix) { fOutputPrefix = aPrefix; } +private: + /* + struct Less : public std::binary_function<Particle, Particle, bool> + { + bool + operator()(const Particle& __x, const Particle& __y) const + { + return __x.Energy < __y.Energy; + } + }; + std::multiset<Particle,Less> fParticles;*/ + std::vector<Particle> fParticles; + std::vector<Particle*> fPrimaryParticles;//stored here to guarantee validity of Particle::SourceParticle field + time_t fLastDebugOutputTime; + int fDebugOutputPeriod; + std::string fOutputPrefix; +}; + +class IResult { +public: + virtual bool AbortRequested() const = 0; + virtual bool ContinuePropagation(const Particle& aParticle) const = 0; + virtual void Add(std::vector<Particle> aParticles) = 0; + virtual cosmo_time GetMinTravelTimeLeft(const Particle& aParticles) const = 0; +}; + +class Result : public IResult{ +public: + Result() : fFilter(0), fIncludeFilterInPropagation(false),fAutoFlushInterval(0),fParticleCounter(0), fStopRequested(false){}; + Result(IFilter* aOutputFilter, bool aIsPropagationFilter = false) : + fFilter(aOutputFilter), fIncludeFilterInPropagation(aIsPropagationFilter),fAutoFlushInterval(0),fParticleCounter(0), fStopRequested(false){}; + Result(double aEmin): + fFilter(new EnergyBasedFilter(aEmin)), fIncludeFilterInPropagation(true),fAutoFlushInterval(0),fParticleCounter(0), fStopRequested(false){} + //set to 0 to disable auto flush + void SetAutoFlushInterval(uint aNumberOfPrimaryParticles); + /*! + @param[in] aParticle particle + @return true if farther propagation simulation is required for the particle, false otherwise + */ + bool ContinuePropagation(const Particle& aParticle) const + { + return GetMinTravelTimeLeft(aParticle)>0 && !(fIncludeFilterInPropagation && fFilter && !fFilter->Pass(aParticle)); + } + inline const CosmoTime& T() const {return fT;} + cosmo_time GetMinTravelTimeLeft(const Particle& aParticles) const; + ~Result(); + void SetEndTime(const CosmoTime& aT); + void Add(std::vector<Particle> aParticles); + void Add(const Particle& aParticle); + void Flush(bool aFinal=true); + void AddOutput(IOutput* aOutput) { fOutputs.push_back(aOutput); } + inline void SetOutputFilter(IFilter* aFilter) {fFilter = aFilter;} + void EnableFlushOnCtrlC(); + bool AbortRequested() const { return fStopRequested; } +private: + static void SIGINT_handler(int); + static std::vector<Result*> fCtrlCFlushList; + CosmoTime fT;//default end time is z=0 (see default constructor of CosmoTime) + SmartPtr<IFilter> fFilter; + std::vector<SmartPtr<IOutput> > fOutputs; + bool fIncludeFilterInPropagation; + uint fAutoFlushInterval; + uint fParticleCounter; + bool fStopRequested; +}; + +} +#endif /* PARTICLESTACK_H */ + diff --git a/src/lib/PhotoDisintegration.cpp b/src/lib/PhotoDisintegration.cpp new file mode 100644 index 0000000..0345798 --- /dev/null +++ b/src/lib/PhotoDisintegration.cpp @@ -0,0 +1,384 @@ +/* + * PhotoDisintegration.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#include "PhotoDisintegration.h" +#include "ParticleStack.h" +#include "TableBackgrounds.h" +#include "PropagationEngine.h" + +Interactions::PhotoDisintegration::PhotoDisintegration(mcray::BackgroundIntegral *aBackground, bool aSampleK): + fBackground(aBackground), + fSampleK(aSampleK) +{ +} + +mcray::RandomInteraction *Interactions::PhotoDisintegration::Clone() const { + return new PhotoDisintegration(fBackground); +} + +Interactions::PhotoDisintegration::~PhotoDisintegration() { + +} + +double Interactions::PhotoDisintegration::Rate(const mcray::Particle &aParticle) const { + if(aParticle.Type<A02 || aParticle.Type>=EndNuclei) + return 0.; + const CPDMLine* canals = fMap.getOutcome(CNucleus::getA(aParticle.Type)); + int noOfCanals = canals?canals->size():0; + if(!noOfCanals) + return 0.; + if(fSampleK){ + SigmaK sigma(canals); + return fBackground->GetRateK(sigma, aParticle); + }else{ + SigmaS sigma(canals); + return fBackground->GetRateS(sigma, aParticle); + } +} + +int Interactions::PhotoDisintegration::UnitTest() +{ + UnitTest(A04, true, true, false, 5e18*units.eV, 1e20*units.eV, 1, 10000); + return 0; +} + +void Interactions::PhotoDisintegration::UnitTest(ParticleType aPrimary, bool aPrintRates, bool aPropagate, bool aSplit, double Emin, double Emax, double Zmax, int nParticles) +{ + //double alphaThinning = 0;//alpha = 1 conserves number of particles on the average; alpha = 0 disables thinning + ParticleStack particles; + Result result(Emin/CNucleus::getA(aPrimary)); + if(aPropagate) + { + std::string out = Particle::Name(aPrimary); + out += aSplit?"_split" : ""; + out += "_Emin"; + out += ToString(Emin); + out += "_Emax"; + out += ToString(Emax); + out += "_Zmax"; + out += ToString(Zmax); + //result.AddOutput(new SpectrumOutput("out_" + out, Emin, pow(10, 0.05))); + result.AddOutput(new RawOutput(out, false, true)); + debug.SetOutputFile("debug_" + out); + } + if(!cosmology.IsInitialized()) + cosmology.Init(); + + CompoundBackground backgr; + double cmbTemp = 2.73/Units::phTemperature_mult/units.Eunit; + + backgr.AddComponent(new PlankBackground(cmbTemp, 1e-3*cmbTemp, 1e3*cmbTemp)); + //backgr.AddComponent(new PlankBackground(cmbTemp, 6.2e-10/units.Eunit, 6.4e-10/units.Eunit));//only central value + + double stepZ = 0.05; + double logStepK = pow(10,0.05); + double epsRel = 1e-3; + + Backgrounds::Kneiske0309EBL* kn0309 = new Backgrounds::Kneiske0309EBL(); + PBackgroundIntegral backgrIebl; + PRandomInteraction interactionEbl; + if(!aSplit) + { + backgr.AddComponent(kn0309); + } + else + { + backgrIebl = new ContinuousBackgroundIntegral(*kn0309, stepZ, logStepK, Zmax, epsRel); + interactionEbl = new Interactions::PhotoDisintegration(backgrIebl); + } + + PBackgroundIntegral backgrI = new ContinuousBackgroundIntegral(backgr, stepZ, logStepK, Zmax, epsRel); + PRandomInteraction interaction = new Interactions::PhotoDisintegration(backgrI); + + PCELInteraction rs = new RedShift(); + double E = Emin; + if(aPrintRates) + { + std::ofstream ratesOut; + std::string ratesFile = ToString(Particle::Name(aPrimary)) + "_rates"; + ratesFile += aSplit ? "_split" : ""; + ratesOut.open(ratesFile.c_str(),std::ios::out); + + ratesOut << "#E\tRedshiftRate\tRate\t\t[E]=1eV [Rate]=1/Mpc\n"; + + for(double E=Emin; E<Emax; E*=logStepK) + { + Particle particle(aPrimary, 0.); + particle.Energy = E / units.Eunit;//MeV + double rate = interaction->Rate(particle); + double redshiftRate = rs->Rate(particle); + if(aSplit) + { + rate += interactionEbl->Rate(particle); + } + double mult = 1./units.Lunit*Units::Mpc_in_cm; + ratesOut << E*1e6 << "\t" << redshiftRate*mult << "\t" << rate * mult << std::endl; + } + } + if(!aPropagate) + return; + + PropagationEngine pe(particles, result); + + pe.AddInteraction(interaction); + + if(aSplit) + { + pe.AddInteraction(interactionEbl); + } + + E=Emax; + Particle primary(aPrimary, Zmax); + primary.Energy = E;//MeV + //primary.Weight = exp(-Emax/E); + //if(primary.Weight>0) + for(int i=0; i < nParticles; i++) + particles.AddPrimary(primary); + //} + + pe.RunMultithreadReleaseOnly(); + if(aSplit) + delete kn0309; +} + + +bool Interactions::PhotoDisintegration::SampleS(const mcray::Particle &aParticle, double &aS, + mcray::Randomizer &aRandomizer) const { + ASSERT(aParticle.Type>=A02 && aParticle.Type<EndNuclei); + const CPDMLine* canals = fMap.getOutcome(CNucleus::getA(aParticle.Type)); + int noOfCanals = canals?canals->size():0; + ASSERT(noOfCanals>0); + SigmaS sigma(canals); + return (fBackground->GetRateAndSampleS(sigma, aParticle, aRandomizer, aS)!=0); +} + +bool Interactions::PhotoDisintegration::SampleK(const mcray::Particle &aParticle, double &aK, + mcray::Randomizer &aRandomizer) const { + ASSERT(aParticle.Type>=A02 && aParticle.Type<EndNuclei); + const CPDMLine* canals = fMap.getOutcome(CNucleus::getA(aParticle.Type)); + int noOfCanals = canals?canals->size():0; + ASSERT(noOfCanals>0); + SigmaK sigma(canals); + return (fBackground->GetRateAndSampleK(sigma, aParticle, aRandomizer, aK)!=0); +} + +bool Interactions::PhotoDisintegration::GetSecondaries(Particle& aParticle, std::vector<Particle>& aSecondaries, Randomizer& aRandomizer) const +{ + if(fSampleK){ + double k = 0.; + if(!SampleK(aParticle, k, aRandomizer)) + return false; + SampleSecondariesWithK(aParticle, aSecondaries, k, aRandomizer); + } + else{ + double s = 0.; + if(!SampleS(aParticle, s, aRandomizer)) + return false; + SampleSecondariesWithS(aParticle, aSecondaries, s, aRandomizer); + } + return true; +} + +void Interactions::PhotoDisintegration::SampleSecondariesWithS(mcray::Particle &aParticle, + std::vector<mcray::Particle> &aSecondaries, double aS, + mcray::Randomizer &aRandomizer) const { + ASSERT(aParticle.Type>=A02 && aParticle.Type<EndNuclei); + double m=aParticle.Mass(); + double k = 0.5*(aS/m-m); + SampleSecondariesWithK(aParticle, aSecondaries, k, aRandomizer); +} + +void Interactions::PhotoDisintegration::SampleSecondariesWithK(mcray::Particle &aParticle, + std::vector<mcray::Particle> &aSecondaries, double k, + mcray::Randomizer &aRandomizer) const { + ASSERT(aParticle.Type>=A02 && aParticle.Type<EndNuclei); + const CPDMLine* canals = fMap.getOutcome(CNucleus::getA(aParticle.Type)); + int noOfCanals = canals?canals->size():0; + ASSERT(noOfCanals>0); + + //select canal + double totSigma = 0.; + std::vector<double> sigma(canals->size(),0.); + size_t iCanal=0; + for (std::vector<CPhotoDisintegrationCanal *>::const_iterator it = canals->begin(); + it != canals->end(); it++) + { + Function* curSigma = (*it)->Sigma(); + double kMinCur = curSigma->Xmin(); + if (kMinCur < k) + totSigma += curSigma->f(k); + sigma[iCanal]=totSigma; + iCanal++; + } + double randSigma = aRandomizer.Rand()*totSigma; + CPhotoDisintegrationCanal* selectedCanal=0; + for (iCanal=0; iCanal<canals->size(); iCanal++) + { + if(sigma[iCanal]>=randSigma) { + selectedCanal = canals->at(iCanal); + break; + } + } + + double secE=0;// energy conservation test (debug only) + + const TIsotope &isotope = selectedCanal->isotope(); + int maxDeltaA = selectedCanal->getMaxDeltaA(); + int A = isotope.A; + ASSERT(CNucleus::getA(aParticle.Type)==A); + ASSERT(maxDeltaA < A); + double autoN = 0.; + double autoP = 0.; + for (int deltaA = 1; deltaA <= maxDeltaA; deltaA++) { + double rate = selectedCanal->getRate(deltaA); + ParticleType prodType=(ParticleType)(aParticle.Type-deltaA);//in case A=2,3 secondary canal for proton (H1) may be duplicated, but it is ok, since summary rate is correct + int deltaZ = isotope.Z - CNucleus::getZ(prodType); + if (deltaZ < 0) {//processes with neutron emission followed by beta decay + deltaZ = 0; + } + int deltaN = deltaA - deltaZ; + autoN += deltaN * rate; + autoP += deltaZ * rate; + if (rate > 0.) { + Particle product = aParticle; + product.Type = prodType; + product.Weight *= rate; + product.Energy *= (product.Mass()/aParticle.Mass());//same gamma factor + aSecondaries.push_back(product); + secE += (product.Energy * product.Weight);// energy conservation test (debug only) + } + } + double rateP = selectedCanal->getRateP(); + double rateN = selectedCanal->getRateN(); + if (rateP == CPhotoDisintegrationCanal::AutoRate) { + rateP = autoP; + }; + if (rateN == CPhotoDisintegrationCanal::AutoRate) { + rateN = autoN; + }; + if (rateN > 0.) { + Particle product = aParticle; + product.Type = Neutron; + product.Weight *= rateN; + product.Energy *= (product.Mass()/aParticle.Mass());//same gamma factor + aSecondaries.push_back(product); + secE += (product.Energy * product.Weight);// energy conservation test (debug only) + } + if (rateP > 0.) { + Particle product = aParticle; + product.Type = Proton; + product.Weight *= rateP; + product.Energy *= (product.Mass()/aParticle.Mass());//same gamma factor + aSecondaries.push_back(product); + secE += (product.Energy * product.Weight); + } + double primE = aParticle.Energy * aParticle.Weight;// energy conservation test (debug only) + ASSERT(MathUtils::RelDifference(secE,primE)<1e-3);// verify energy conservation +} + +Interactions::PhotoDisintegration::SigmaS::SigmaS(const CPDMLine* canals) : +fCanals(canals), +fXmin(1e300), +fXmax(0), +fM(-1.) +{ + if(fCanals->size()) { + double kMin = 1e300; + double kMax = 0; + fM = Particle::Mass(fCanals->at(0)->getParticle()); + for (std::vector<CPhotoDisintegrationCanal *>::const_iterator it = fCanals->begin(); + it != fCanals->end(); it++) + { + double kMinCur = (*it)->Sigma()->Xmin(); + if (kMinCur < kMin) + kMin = kMinCur; + double kMaxCur = (*it)->Sigma()->Xmax(); + if (kMaxCur > kMax) + kMax = kMaxCur; + } + fXmin = fM * (fM + 2. * kMin); + fXmax = fM * (fM + 2. * kMax); + } +} + +double Interactions::PhotoDisintegration::SigmaS::f(double s) const { + if(s<=fXmin) + return 0.; + double k = 0.5*(s/fM-fM); + double sigma = 0.; + for (std::vector<CPhotoDisintegrationCanal *>::const_iterator it = fCanals->begin(); + it != fCanals->end(); it++) + { + Function* curSigma = (*it)->Sigma(); + if (curSigma->Xmin() <= k && curSigma->Xmax() >= k) { + sigma += curSigma->f(k); + } + } + return sigma; +} + + +Interactions::PhotoDisintegration::SigmaK::SigmaK(const CPDMLine* canals) : + fCanals(canals), + fXmin(1e300), + fXmax(0) +{ + if(fCanals->size()) { + double kMin = 1e300; + double kMax = 0; + for (std::vector<CPhotoDisintegrationCanal *>::const_iterator it = fCanals->begin(); + it != fCanals->end(); it++) + { + double kMinCur = (*it)->Sigma()->Xmin(); + if (kMinCur < kMin) + kMin = kMinCur; + double kMaxCur = (*it)->Sigma()->Xmax(); + if (kMaxCur > kMax) + kMax = kMaxCur; + } + fXmin = kMin; + fXmax = kMax; + ASSERT(kMin>0); + } +} + +double Interactions::PhotoDisintegration::SigmaK::f(double k) const { + if(k<=fXmin || k>fXmax) + return 0.; + + double sigma = 0.; + for (std::vector<CPhotoDisintegrationCanal *>::const_iterator it = fCanals->begin(); + it != fCanals->end(); it++) + { + Function* curSigma = (*it)->Sigma(); + if (curSigma->Xmin() <= k && curSigma->Xmax() >= k) { + sigma += curSigma->f(k); + } + } + return sigma; +} \ No newline at end of file diff --git a/src/lib/PhotoDisintegration.h b/src/lib/PhotoDisintegration.h new file mode 100644 index 0000000..b34ef13 --- /dev/null +++ b/src/lib/PhotoDisintegration.h @@ -0,0 +1,86 @@ +/* + * PhotoDisintegration.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#ifndef MCRAY_PHOTODISINTEGRATION_H +#define MCRAY_PHOTODISINTEGRATION_H + +#include "Interaction.h" +#include "Background.h" +#include "Nucleus.h" + +namespace Interactions { + using namespace mcray; + + class PhotoDisintegration : public RandomInteraction { + class SigmaS : public Function + { + public: + SigmaS(const CPDMLine* canals); + double f(double s) const; + double Xmin() const {return fXmin;} + double Xmax() const {return fXmax;} + private: + const CPDMLine* fCanals; + double fXmin; + double fXmax; + double fM; + }; + class SigmaK : public Function + { + public: + SigmaK(const CPDMLine* canals); + double f(double s) const; + double Xmin() const {return fXmin;} + double Xmax() const {return fXmax;} + private: + const CPDMLine* fCanals; + double fXmin; + double fXmax; + }; + public: + PhotoDisintegration(BackgroundIntegral* aBackground, bool aSampleK=true); + RandomInteraction* Clone() const; + virtual ~PhotoDisintegration(); + double Rate(const Particle& aParticle) const; + static void UnitTest(ParticleType aPrimary, bool aPrintRates, bool aPropagate, bool aSplit, double aEmin, double aEmax, double aZmax, int nParticles=1000); + static int UnitTest(); + bool SampleS(const Particle& aParticle, double& aS, Randomizer& aRandomizer) const; + bool GetSecondaries(Particle& aParticle, std::vector<Particle>& aSecondaries, Randomizer& aRandomizer) const; + void SampleSecondaries(Particle& aParticle, std::vector<Particle>& aSecondaries, double aL, Randomizer& aRandomizer) const; + bool SampleK(const Particle& aParticle, double& aS, Randomizer& aRandomizer) const; + void SampleSecondariesWithS(Particle &aParticle, std::vector<Particle> &aSecondaries, double aS, Randomizer &aRandomizer) const; + void SampleSecondariesWithK(Particle &aParticle, std::vector<Particle> &aSecondaries, double aK, Randomizer &aRandomizer) const; + + private: + CPhotoDisintegrationMap fMap; + SmartPtr<BackgroundIntegral> fBackground; + bool fSampleK; + }; +} + +#endif //MCRAY_PHOTODISINTEGRATION_H diff --git a/src/lib/PrecisionTests.cpp b/src/lib/PrecisionTests.cpp new file mode 100644 index 0000000..4168e41 --- /dev/null +++ b/src/lib/PrecisionTests.cpp @@ -0,0 +1,72 @@ +/* + * PrecisionTests.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * PrecisionTests.cpp + * + * Created on: Dec 25, 2011 + * Author: ok + */ + +#include "PrecisionTests.h" +#include "../external/mpfrc++/mpreal.h" +#include "Utils.h" + +using namespace mpfr; + +namespace Test{ + +void PrecisionTests::SetPrecision(int aPrecision) +{ + mpreal::set_default_prec(aPrecision); +} + +double PrecisionTests::PartialSigmaAccurate(double rMin, double s) +{ +/* +Formula for partial cross section was taken from http://lanl.arxiv.org/abs/1106.5508v1 eq. (9) +*/ + mpreal yMin=1./s;//s here is in units of m^2 + mpreal yMax = 1-rMin; + if(yMin>=yMax) + { + ASSERT((yMax-yMin)/(yMax+yMin)<1e-10); + return 0.; + } + + mpreal y_1 = 1.-yMin; + mpreal y_1_2 = y_1*y_1; + mpreal a=yMax-yMin; + mpreal result = 0; + + result = yMin*(yMax-yMin)/y_1*(log(yMax/yMin)/(yMax-yMin)*(1.-4.*yMin*(1.+yMin)/y_1_2)+4.*(yMin/yMax+yMin)/y_1_2+0.5*(yMax+yMin)); + ASSERT_VALID_NO(result.toDouble()); + + return result.toDouble(); +} + +}//namespace Test diff --git a/src/lib/PrecisionTests.h b/src/lib/PrecisionTests.h new file mode 100644 index 0000000..d5a6147 --- /dev/null +++ b/src/lib/PrecisionTests.h @@ -0,0 +1,49 @@ +/* + * PrecisionTests.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#ifndef PRECISIONTESTS_H_ +#define PRECISIONTESTS_H_ + +#ifdef USE_MPFR + +#include "mpfrc++/mpreal.h" + +namespace Test +{ + +class PrecisionTests { +public: + static void SetPrecision(int aPrecision); + static double PartialSigmaAccurate(double rMin, double s); +}; + +} +#endif //#ifdef USE_MPFR + + +#endif /* PRECISIONTESTS_H_ */ diff --git a/src/lib/PropagationEngine.cpp b/src/lib/PropagationEngine.cpp new file mode 100644 index 0000000..7daaead --- /dev/null +++ b/src/lib/PropagationEngine.cpp @@ -0,0 +1,521 @@ +/* + * PropagationEngine.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <math.h> +#include <numeric> +#include "PropagationEngine.h" +#include <omp.h> + +using namespace std; + +namespace mcray +{ + +unsigned long int PropagationEngine::fLastRandSeed = 0; + +PropagationEngine::PropagationEngine(ParticleStack &aStack, IResult &aResult, unsigned long int aRandSeed) : + fThinning(0), + fFilter(0), + fStack(aStack), + fResult(aResult), + fRandomizer(aRandSeed ? aRandSeed:fLastRandSeed), + fAccuracy(0.01), + fMinStep(0.), + fInfoPrinted(false), + fLogger(0) +{ + if(!aRandSeed) + fLastRandSeed++; + +} + +PropagationEngine::PropagationEngine(PropagationEngine &aEngine, unsigned long int aRandSeed) : + fThinning(aEngine.fThinning), + fFilter(aEngine.fFilter), + fStack(aEngine.fStack), + fResult(aEngine.fResult), + fRandomizer(aRandSeed ? aRandSeed:fLastRandSeed), + fAccuracy(aEngine.fAccuracy), + fMinStep(aEngine.fMinStep), + fLogger(aEngine.fLogger) +{ + if(!aRandSeed) + fLastRandSeed++; + for(vector<PCELInteraction>::iterator it = aEngine.fContinuousEnergyLosses.begin(); it != aEngine.fContinuousEnergyLosses.end(); it++) + { + fContinuousEnergyLosses.push_back((*it)->Clone()); + } + for(vector<PDeflectionInteraction>::iterator it = aEngine.fDeflectionInteractions.begin(); it != aEngine.fDeflectionInteractions.end(); it++) + { + fDeflectionInteractions.push_back((*it)->Clone()); + } + for(vector<PRandomInteraction>::iterator it = aEngine.fRandomInteractions.begin(); it != aEngine.fRandomInteractions.end(); it++) + { + fRandomInteractions.push_back((*it)->Clone()); + } + for(vector<double>::iterator it = aEngine.fRandomInteractionRateMultipliers.begin(); it != aEngine.fRandomInteractionRateMultipliers.end(); it++) + { + fRandomInteractionRateMultipliers.push_back(*it); + } +} + +void PropagationEngine::Run(double aMaxRateVariation) { + try { + Particle p(Electron, 0.);//will be reset by fStack.Pop + while (fStack.Pop(p)) { + try { + while (true) { + if(fLogger) + fLogger->log(p); // record initial state + if (aMaxRateVariation > 0) + RunParticleWithVariableRate(p, aMaxRateVariation); + else + RunParticle(p); + if(fResult.AbortRequested()) + return; + if (!fSecondaryParticles.size()) + break; + p = fSecondaryParticles.back(); + fSecondaryParticles.pop_back(); + } + + }catch(Exception* ex){ + fFinalParticles.clear();//don't save current paricle cascade + std::string message = ex->Message() + "\tprimary " + p.ToString(); + LOG_ERROR(message); + std::cerr << message << std::endl; + } + if(fFinalParticles.size()){ + fResult.Add(fFinalParticles); + fFinalParticles.clear(); + } + } + }catch(Exception* ex) + { + LOG_ERROR(ex->Message()); + std::cerr << ex->Message() << std::endl; + } +} + +void PropagationEngine::RunMultithreadReleaseOnly(double aMaxRateVariation, int aNumberOfThreads) +{ +#ifdef _DEBUG + Run(aMaxRateVariation); +#else + RunMultithread(aMaxRateVariation, aNumberOfThreads); +#endif +} + +void PropagationEngine::RunMultithread(double aMaxRateVariation, int aNumberOfThreads) +{ + if(aNumberOfThreads>1) + omp_set_num_threads(aNumberOfThreads); + else if(aNumberOfThreads==1){ + Run(aMaxRateVariation); + return; + } +#pragma omp parallel +{ + SafePtr<PropagationEngine> pe; +#pragma omp critical (PropagationEngine) + { + pe = new PropagationEngine(*this, fRandomizer.CreateIndependent()); + } + +#pragma omp barrier + +#pragma omp master +{ + if(!fInfoPrinted){ + int nthreads = omp_get_num_threads(); + cout << "Running in " << nthreads << " threads" << '\n'; + fInfoPrinted = true; + } +} + + pe->Run(aMaxRateVariation); +} +} + +void PropagationEngine::move(Particle &aParticle, cosmo_time aDt, double aElossRate) +{ + cosmo_time eLossHalf = exp(-0.5 * aElossRate * aDt);//relative energy loss during dt/2 + aParticle.Energy = aParticle.Energy * eLossHalf;//mean energy during period [t, t+dt] + bool deflected = false; + for(vector<PDeflectionInteraction>::iterator it = fDeflectionInteractions.begin(); it != fDeflectionInteractions.end(); it++) + { + if((*it)->Propagate(aDt, aParticle, fRandomizer))//should not modify aParticle.Energy and aParticle.Time + { + if(deflected) + Exception::Throw("Particle treated by more than one deflection interaction"); + deflected = true; + } + } + if(!deflected) + aParticle.PropagateFreely(aDt); + aParticle.Energy = aParticle.Energy * eLossHalf; + if(fLogger) + fLogger->log(aParticle); +} + +double PropagationEngine::GetInteractionRate(const Particle &aParticle) const { + double totRate = 0.; + for(vector<PRandomInteraction>::const_iterator it = fRandomInteractions.begin(); it != fRandomInteractions.end(); it++) + { + double rate = (*it)->Rate(aParticle); + totRate += rate; + } + return totRate; +} + +void PropagationEngine::RunParticle(Particle& aParticle) +{ + while((!fResult.AbortRequested()) && fResult.ContinuePropagation(aParticle)) + { + if(fFilter && !fFilter->Pass(aParticle)) + return; + double eLossRate = 0.; + //double bRate = 0.; + double randRate = 0.; + cosmo_time maxDt = fResult.GetMinTravelTimeLeft(aParticle); + ASSERT(maxDt>0); + for(vector<PCELInteraction>::iterator it = fContinuousEnergyLosses.begin(); it != fContinuousEnergyLosses.end(); it++) + { + eLossRate += (*it)->Rate(aParticle); + } + //for(vector<PDeflectionInteraction>::iterator it = fDeflectionInteractions.begin(); it != fDeflectionInteractions.end(); it++) + //{ + // bRate += (*it)->Rate(aParticle); + //} + vector<double> rates; + for(vector<PRandomInteraction>::iterator it = fRandomInteractions.begin(); it != fRandomInteractions.end(); it++) + { + double rate = (*it)->Rate(aParticle); + randRate += rate; + rates.push_back(rate); + } + cosmo_time dt = eLossRate>0 ? fAccuracy/eLossRate : maxDt;//fAccuracy<1 + if(dt>maxDt) + dt = maxDt; + + double rand = -1; + bool hasInteraction = false; + if(randRate>0) + { + double freePath = fRandomizer.GetFreePath(1./randRate, rand); + if(freePath<=dt) + { + dt = freePath; + hasInteraction = true; + } + } + + move(aParticle, dt, eLossRate); + + + for(vector<PCELInteraction>::iterator it = fContinuousEnergyLosses.begin(); it != fContinuousEnergyLosses.end(); it++) + { + vector<Particle> secondaries; + (*it)->GetSecondaries(aParticle,secondaries,dt,fRandomizer); + HandleSecondaries(aParticle, secondaries, 1.); + } + + if(hasInteraction) + { + //select interaction + double r = fRandomizer.Rand() * randRate; + double totRate = 0.; + RandomInteraction* interaction = 0; + for(unsigned int i=0; i<rates.size(); i++) + { + totRate += rates[i]; + if(totRate>=r) + { + interaction = fRandomInteractions[i]; + break; + } + } + uint maxError_count = 3; + bool completed = false; + vector<Particle> secondaries; + bool interactionOccurred = false; + for(uint i=0; !completed; i++) + { + try { + + interactionOccurred = interaction->GetSecondaries(aParticle, secondaries, + fRandomizer);//may return false if particle energy is close to process threshold + + completed = true; + }catch(Exception* ex) + { + if(i==maxError_count) { + std::cerr << "recovery failed after " << maxError_count-1 << "attempts" << std::endl; + throw ex; + } + std::cerr << ex->Message() << "\n\tattempting to recover" << std::endl; + secondaries.clear(); + } + } + if(interactionOccurred) { + HandleSecondaries(aParticle, secondaries, 1.);//todo: implement calculation speedup for rare processes using rate multiplier in RunParticle (see implementation in RunParticleWithVariableRate) + if (interaction->KillsPrimary()) + return; + } + } + } + fFinalParticles.push_back(aParticle); +} + +void PropagationEngine::RunParticleWithVariableRate(Particle& aParticle, double aMaxRelRateError){ + const int maxNumberOfStepAdjustments = 50;//2e-50 ~ 1e-15 which is double type accuracy + LOG_VERBOSE("RPWVR:0\t" + aParticle.ToString()); + while((!fResult.AbortRequested()) && fResult.ContinuePropagation(aParticle)) + { + LOG_VERBOSE("RPWVR:1\t" + aParticle.ToString()); + if(fFilter && !fFilter->Pass(aParticle)) + return; + + double eLossRate1 = 0.;//total continuous energy loss rate at start point + + cosmo_time maxDt = fResult.GetMinTravelTimeLeft(aParticle);//maximal step + ASSERT(maxDt>0); + + vector<double> celRates1(fContinuousEnergyLosses.size(),0.);// continuous energy loss rates for separate processes at start point + int iRate = 0; + for(vector<PCELInteraction>::iterator it = fContinuousEnergyLosses.begin(); it != fContinuousEnergyLosses.end(); it++) + { + double rate = (*it)->Rate(aParticle); + celRates1[iRate++] = rate; + eLossRate1 += rate; + } + + cosmo_time dtCEL = eLossRate1 >0 ? fAccuracy/ eLossRate1 : maxDt;//fAccuracy<1 + if(dtCEL >maxDt) + dtCEL = maxDt; + vector<double> celRates2(fContinuousEnergyLosses.size(), 0.); + //find optimal step for CEL interaction + Particle part2 = aParticle; + LOG_VERBOSE("RPWVR:2\t" + aParticle.ToString() + "\tdtCel = " + ToString(dtCEL/units.Mpc)); + for(int nSteps=0; true; nSteps++) { + move(part2, dtCEL, eLossRate1); + LOG_VERBOSE("RPWVR:2.1\t" + part2.ToString() + "\tdtCel = " + ToString(dtCEL/units.Mpc)); + double eLossRate2 = 0.; + iRate = 0; + for (vector<PCELInteraction>::iterator it = fContinuousEnergyLosses.begin(); + it != fContinuousEnergyLosses.end(); it++) { + celRates2[iRate] = (*it)->Rate(part2); + eLossRate2 += celRates2[iRate]; + LOG_VERBOSE("RPWVR:2.2\t" + part2.ToString() + "\tRelDiffCel[" + ToString(iRate) + "] = " + ToString(MathUtils::RelDifference(celRates2[iRate],celRates1[iRate]))); + iRate++; + } + double relDif = MathUtils::RelDifference(eLossRate1,eLossRate2); + LOG_VERBOSE("RPWVR:2.2\t" + part2.ToString() + "\tRelDiffCel = " + ToString(relDif)); + if(relDif<aMaxRelRateError) + break; + if(dtCEL<=fMinStep){ + LOG_WARNING("CEL rate variance " + ToString(relDif) + " (limitted by step size)") + break; + } + if(nSteps==maxNumberOfStepAdjustments){ + Exception::Throw("Unable to achieve desired CEL rate variance. Minimal value " + ToString(relDif) + "\nSet minimal step and/or decrease desired accuracy"); + } + else// adjust step and repeat + { + dtCEL *= (0.5 * aMaxRelRateError / relDif); + part2 = aParticle; + } + }; + LOG_VERBOSE("RPWVR:3\t" + aParticle.ToString() + "\tdtCel = " + ToString(dtCEL/units.Mpc)); + double randRate1 = 0.; + vector<double> randRates1(fRandomInteractions.size()); + iRate = 0; + for(vector<PRandomInteraction>::iterator it = fRandomInteractions.begin(); it != fRandomInteractions.end(); it++) + { + double rate = (*it)->Rate(aParticle)*fRandomInteractionRateMultipliers[iRate]; + randRate1 += rate; + randRates1[iRate++] = rate; + } + + vector<double> randRates2(fRandomInteractions.size(),0); + //double randRate2 = 0.; + double dtRand = dtCEL; + //vector<double> maxValidStep(fRandomInteractions.size(), dtCEL); + iRate = 0; + double relDif = -1.; + for(int nSteps=0; true; nSteps++) { + LOG_VERBOSE("RPWVR:4\t" + part2.ToString() + "\tdtRand/dtCEL = " + ToString(dtRand/dtCEL)); + for(; iRate < fRandomInteractions.size(); iRate++) + { + randRates2[iRate] = fRandomInteractions[iRate]->Rate(part2)*fRandomInteractionRateMultipliers[iRate]; + relDif = MathUtils::RelDifference(randRates1[iRate],randRates2[iRate]); + if(relDif>aMaxRelRateError){ + LOG_VERBOSE("RPWVR:4.1\tRelDiffCel[" + ToString(iRate) + "] = " + ToString(relDif)); + double dtMult = 0.5*aMaxRelRateError/relDif; + dtRand *= dtMult; + for(int iPrevIntr=0; iPrevIntr <iRate; iPrevIntr++){// (using linear interpolation to avoid recalculation of well known rates) + randRates2[iPrevIntr] = randRates1[iPrevIntr] + (randRates2[iPrevIntr]-randRates1[iPrevIntr])*dtMult; + } + break; + } + } + if(iRate==fRandomInteractions.size())//all tests passed + break; + + if(dtRand<=fMinStep){ + LOG_WARNING("Rate variance " + ToString(relDif) + " (limitted by step size)") + break; + } + if(nSteps == maxNumberOfStepAdjustments){ + Exception::Throw("Unable to achieve desired rate variance. Minimal value " + ToString(relDif) + "\nSet minimal step and/or decrease desired accuracy"); + } + //else, adjust step and repeat + part2 = aParticle; + move(part2, dtRand, eLossRate1); + }; + LOG_VERBOSE("RPWVR:5\t" + aParticle.ToString() + "\tdtRand = " + ToString(dtRand/units.Mpc)); + double randRate2 = std::accumulate(randRates2.begin(), randRates2.end(), 0.); + + double rand = -1; + RandomInteraction* interaction = 0; + double sec_weight = 1.; + double sqrt_noninteracting_weight = 1.; + double meanRandRate = 0.5*(randRate1+randRate2); + double dt = dtRand; + if(meanRandRate > 0) + { + double freePath = fRandomizer.GetFreePath(1./ meanRandRate, rand); + if(freePath <= dtRand) + { + dt = freePath; + //calculate mean rate and save it to randRates1 vector + double sumRates = 0.; + for(iRate = fRandomInteractions.size()-1; iRate>=0; iRate--){ + randRates1[iRate] += (dt/dtRand*(randRates2[iRate]-randRates1[iRate])); + sumRates += randRates1[iRate]; + } + //select interaction + double r = fRandomizer.Rand() * sumRates; + double totRate = 0.; + for(size_t i=0; i< randRates1.size(); i++) + { + totRate += randRates1[i]; + if(totRate>=r) + { + interaction = fRandomInteractions[i]; + sec_weight = 1./fRandomInteractionRateMultipliers[i]; + break; + } + } + } + else{ + double ln_noninteracting_weight = 0.; + for(iRate = fRandomInteractions.size()-1; iRate>=0; iRate--){ + if(fRandomInteractionRateMultipliers[iRate]!=1.){ + ln_noninteracting_weight += (0.5*(randRates2[iRate]+randRates1[iRate])*(1.-1./fRandomInteractionRateMultipliers[iRate])); + } + } + sqrt_noninteracting_weight = exp(0.5*ln_noninteracting_weight); + } + + } + LOG_VERBOSE("RPWVR:6\t" + aParticle.ToString()+ "\tdt = " + ToString(dt/units.Mpc)); + aParticle.Weight *= sqrt_noninteracting_weight; + move(aParticle, 0.5*dt, eLossRate1); + for(vector<PCELInteraction>::iterator it = fContinuousEnergyLosses.begin(); it != fContinuousEnergyLosses.end(); it++) + { + vector<Particle> secondaries; + (*it)->GetSecondaries(aParticle,secondaries, dt,fRandomizer); + HandleSecondaries(aParticle, secondaries, 1.);//currently continuous energy loss weighting is not implemented + } + move(aParticle, 0.5*dt, eLossRate1); + aParticle.Weight *= sqrt_noninteracting_weight; + LOG_VERBOSE("RPWVR:6.1\t" + aParticle.ToString()); + + if(interaction) + { + uint maxError_count = 3; + bool completed = false; + vector<Particle> secondaries; + bool interactionOccurred = false; + for(uint i=0; !completed; i++) + { + LOG_VERBOSE("RPWVR:7\t" + aParticle.ToString()); + try { + + interactionOccurred = interaction->GetSecondaries(aParticle, secondaries, + fRandomizer);//may return false if particle energy is close to process threshold + + completed = true; + }catch(Exception* ex) + { + if(i==maxError_count) { + LOG_ERROR("recovery failed after " + ToString(maxError_count-1) + " attempts"); + throw ex; + } + LOG_WARNING(ex->Message() + "\tattempting to recover"); + secondaries.clear(); + } + } + if(interactionOccurred) { + LOG_VERBOSE("RPWVR:8\t" + aParticle.ToString()); + HandleSecondaries(aParticle, secondaries, sec_weight); + if (interaction->KillsPrimary()){ + LOG_VERBOSE("RPWVR:-2\t" + aParticle.ToString()); + return; + } + } + } + LOG_VERBOSE("RPWVR:9\t" + aParticle.ToString()); + } + LOG_VERBOSE("RPWVR:10\t" + aParticle.ToString()); + fFinalParticles.push_back(aParticle); + LOG_VERBOSE("RPWVR:-1\t" + aParticle.ToString()); +} + +void PropagationEngine::HandleSecondaries(Particle& aPrimary, vector<Particle>& aSecondaries, double aWeightMultiplier) +{ + long numberOfInteractions = aPrimary.Ninteractions + 1; + if(fFilter) + for(int i=aSecondaries.size()-1; i>=0; i--) { + aSecondaries[i].Weight *= aWeightMultiplier; + if (!fFilter->Pass(aSecondaries[i])) { + LOG_VERBOSE("HS:filter\t" + (aSecondaries.begin() + i)->ToString()); + aSecondaries.erase(aSecondaries.begin() + i); + } + } + if(fThinning) + fThinning->Run(aSecondaries, fRandomizer); + for(std::vector<Particle>::iterator it = aSecondaries.begin(); it != aSecondaries.end(); it++) + { + it->SetParent(aPrimary); + it->Ninteractions = numberOfInteractions; + fSecondaryParticles.push_back(*it); + } +} + +} diff --git a/src/lib/PropagationEngine.h b/src/lib/PropagationEngine.h new file mode 100644 index 0000000..1fa1179 --- /dev/null +++ b/src/lib/PropagationEngine.h @@ -0,0 +1,92 @@ +/* + * PropagationEngine.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#ifndef PROPAGATIONENGINE_H +#define PROPAGATIONENGINE_H + +#include <vector> +#include "Interaction.h" +#include "Thinning.h" +#include "Filter.h" +#include "ParticleStack.h" +#include "Randomizer.h" +#include "Utils.h" +#include "Logger.h" + +namespace mcray +{ + +class PropagationEngine { +public: + PropagationEngine(ParticleStack &aStack, IResult &aResult, unsigned long int aRandSeed=0); + PropagationEngine(PropagationEngine &aCloneFrom, unsigned long int aRandSeed=0); + void Run(double aMaxRateVariation=0); + void RunMultithread(double aMaxRateVariation=0, int aNumberOfThreads = 0); + void RunMultithreadReleaseOnly(double aMaxRateVariation=0, int aNumberOfThreads = 0); + inline void SetThinning(IThinning* aThinning) {fThinning = aThinning;} + + /// Set propagation filter (particles not passing the filter will be erased) + inline void SetPropagationFilter(IFilter* aFilter) {fFilter = aFilter;} + inline void AddInteraction(PCELInteraction aInt) {fContinuousEnergyLosses.push_back(aInt);} + inline void AddInteraction(PDeflectionInteraction aInt) {fDeflectionInteractions.push_back(aInt);} + inline void AddInteraction(PRandomInteraction aInt, double aRateMultiplier=1.) {//todo: implement calculation speedup for rare processes using rate multiplier in RunParticle (see implementation in RunParticleWithVariableRate) + fRandomInteractions.push_back(aInt); + fRandomInteractionRateMultipliers.push_back(aRateMultiplier); + } + //set minimal step for variable rate mode + inline void SetMinimalStep(double aMinStep) { fMinStep = aMinStep; } + inline void SetLogger(ILogger* aLogger){fLogger=aLogger;} + double GetInteractionRate(const Particle &aParticle) const; +private: + void RunParticle(Particle& aParticle); + void move(Particle &aParticle, cosmo_time aDt, double aElossRate); + + //special mode for highly variable rates + void RunParticleWithVariableRate(Particle& aParticle, double aMaxRelRateError); + void HandleSecondaries(Particle& aPrimary, std::vector<Particle>& aSecondaries, double aWeightMultiplier); + std::vector<PRandomInteraction> fRandomInteractions; + std::vector<PDeflectionInteraction> fDeflectionInteractions; + std::vector<PCELInteraction> fContinuousEnergyLosses; + std::vector<double> fRandomInteractionRateMultipliers;//useful for very rare interactions + IThinning *fThinning; + SmartPtr<IFilter> fFilter; + ParticleStack &fStack; + IResult &fResult; + Randomizer fRandomizer; + double fAccuracy; + double fMinStep; + std::vector<Particle> fSecondaryParticles; + std::vector<Particle> fFinalParticles; + static unsigned long int fLastRandSeed; + bool fInfoPrinted; + ILogger* fLogger; +}; + +} +#endif /* PROPAGATIONENGINE_H */ + diff --git a/src/lib/ProtonPP.cpp b/src/lib/ProtonPP.cpp new file mode 100644 index 0000000..5c8791d --- /dev/null +++ b/src/lib/ProtonPP.cpp @@ -0,0 +1,341 @@ +/* + * ProtonPP.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "ProtonPP.h" +#include <fstream> + +//used for unit test only +#include "PropagationEngine.h" + +namespace Interactions { +using namespace mcray; + +ProtonPPSigma::ProtonPPSigma() +{ + double Me = Particle::Mass(Electron); + double Mp = Particle::Mass(Proton); + fMpMe_1 = 1./(Me*Mp); + fThresholdS=2.*Me + Mp; + fThresholdS *= fThresholdS; + fMp2 = Mp*Mp; + fCoef = AlphaEM*AlphaEM*AlphaEM/Me/Me; //alpha*Z^2*r_0^2 == alpha^3*Z^2/m_e^2 +} + +double ProtonPPSigma::sigmaLab(double k) const +{ + double result = 0; + if(k<4)//using formula (A1) from M. J. Chodorowski, A. A. Zdziarski, and M. Sikora, Astrophys. J. 400, 181 (1992) + { + double eta = (k - 2.)/(k+2); + double eta2 = eta*eta; + double kk = (k - 2.)/k; + //2pi/3*fCoef * kk^3 * (1+1/2 eta + 23/40 eta^2 + 37/120 eta^3 + 61/192 eta^4) + result = 2.0943951023932*fCoef*kk*kk*kk*(1.+0.5*eta+0.575*eta2+ + 0.308333333333333*eta2*eta+0.317708333333333*eta2*eta2); + } + else + {//using formula (A2) from M. J. Chodorowski, A. A. Zdziarski, and M. Sikora, Astrophys. J. 400, 181 (1992) + double ln2k = log(2.*k); + double ln2k2 = ln2k*ln2k; + double coef2 = 6.*ln2k + 0.666666666666667*ln2k2*ln2k-ln2k2-3.28986813369645*ln2k + 0.549047873167415; + double coef4 = -(0.1875*ln2k+0.125); + double coef6 = -(0.0125868055555556*ln2k-0.00557002314814815); + double r2 = 4./k/k; + double r4 = r2*r2; + result = fCoef*(3.11111111111111*ln2k-8.07407407407407+r2*coef2+r4*coef4+r2*r4*coef6); + } + ASSERT_VALID_NO(result); + return result; +} + +double ProtonPPSigma::f(double s) const +{ + if(s<=fThresholdS) + return 0.; + + double k = 0.5*(s - fMp2)*fMpMe_1; // photon energy in proton rest frame in units of electron mass + return sigmaLab(k); +} + +ProtonPP::ProtonPP(BackgroundIntegral* aBackground): +fBackground(aBackground) +{ +} + +ProtonPP::~ProtonPP() { +} + +double ProtonPP::Rate(const Particle& aParticle) const +{ + if(aParticle.Type != Proton) + return 0.; + return fBackground->GetRateS(fSigma, aParticle); +} + +bool ProtonPP::SampleS(const Particle& aParticle, double& aS, Randomizer& aRandomizer) const +{ + ASSERT(aParticle.Type == Proton); + aS = 0.; + return (fBackground->GetRateAndSampleS(fSigma, aParticle, aRandomizer, aS)!=0); +} + +double ProtonPP::maxPPPemaxError = 0.; + +void ProtonPP::SampleSecondaries(Particle& aParticle, std::vector<Particle>& aSecondaries, double aS, Randomizer& aRandomizer) const +{ + ASSERT(aParticle.Type == Proton); + Particle secProton = aParticle; + Particle secElectron = aParticle; + Particle secPositron = aParticle; + secElectron.Type = Electron; + secPositron.Type = Positron; + double E = aParticle.Energy; + double Mp = Particle::Mass(Proton); + double k = 0.5*(aS/Mp-Mp); + double Me = Particle::Mass(Electron); + double betaP = aParticle.beta(); + double gammaP = E / Mp; + double mFrac = Mp/Me; + double km = k/Me; + double c1 = mFrac*km-2.; + + ///formula (3.28) J. W. Motz, H. A. Olsen, and H. W. Koch, Rev. Mod. Phys. 41, 581 (1969). + double pMaxProtonLab = (km*(mFrac*(km+mFrac)-2)+(km+mFrac)*sqrt(c1*c1-4.*mFrac*mFrac))/mFrac/(2.*km+mFrac)*Me; + ASSERT_VALID_NO(pMaxProtonLab); + double EmaxProtonLab = sqrt(pMaxProtonLab*pMaxProtonLab+Mp*Mp); + double EminProton = gammaP*(EmaxProtonLab - betaP*pMaxProtonLab); + + ///formula (3.43a) J. W. Motz, H. A. Olsen, and H. W. Koch, Rev. Mod. Phys. 41, 581 (1969). + double c2 = km+mFrac; + double c3 = mFrac*c2*(km-1.); + double c4 = km*sqrt(km*km + mFrac*mFrac*km*(km-2.)); + double c5 = (c2*c2-km*km); + + double EeLabMaxAntiparallelToK = (c3-c4)/c5*Me; + double EeLabMaxParallelToK = (c3+c4)/c5*Me; + + double PeLabMaxAntiparallelToK = (EeLabMaxAntiparallelToK>1.001*Me) ? sqrt(EeLabMaxAntiparallelToK*EeLabMaxAntiparallelToK - Me*Me) : 0.; + double PeLabMaxParallelToK = (EeLabMaxParallelToK>1.001*Me) ? sqrt(EeLabMaxParallelToK*EeLabMaxParallelToK - Me*Me) : 0.; + double Emin = gammaP*(EeLabMaxParallelToK - betaP*PeLabMaxParallelToK); + double Emax = gammaP*(EeLabMaxAntiparallelToK + betaP*PeLabMaxAntiparallelToK); + if(Emin>Emax) + {//this may occur if EeLabMaxAntiparallelToK << Me, i.e. close to threshold + ASSERT(EeLabMaxAntiparallelToK<1.001*Me); + double tmp = Emin; + Emin = Emax; + Emax = tmp; + } + + ASSERT(Emin>Me); + ASSERT(Emax<E); + + if(E < (EminProton+Emax+Emin)) + { + //ASSERT(EminProton+Emax+Emin<E); + double correctedEmax = E - EminProton - Emin; + double relError = (Emax-correctedEmax)/Emax; + if(relError>maxPPPemaxError) + { + std::cerr << "PPP Emax error " << relError << std::endl; + maxPPPemaxError = relError; + } + Emax = correctedEmax; + } + + double E1 = SampleE(Emin, Emax, aRandomizer); + double E2max = E - E1 - EminProton; + if(E2max>Emax) + E2max = Emax; + double E2 = SampleE(Emin, E2max, aRandomizer); + if(aRandomizer.Rand()>0.5) + {//Randomly choose which particle has energy E1 and which E2 + double tmp = E1; + E1 = E2; E2 = tmp; + } + secElectron.Energy = E1; + secPositron.Energy = E2; + secProton.Energy = E - E1 - E2; + aSecondaries.push_back(secElectron); + aSecondaries.push_back(secPositron); + aSecondaries.push_back(secProton); +} + +void ProtonPP::UnitTest() +{ + double Zmax = 1; + double Emin = 1e6*units.eV; + double Emax = 1e19*units.eV; + int Nparticles = 1000; + std::string outputDir = "testProtonPP"; + unsigned int kAc = 1; + double epsRel = 1e-3; + double stepZ = Zmax<0.05 ? Zmax/2 : 0.025; + double logStepK = pow(10,0.05/kAc); + if(!cosmology.IsInitialized()) + cosmology.Init(Zmax + 10); + double cmbTemp = 2.73*units.K; + double alphaThinning = 0.5; + CompoundBackground backgr; + backgr.AddComponent(new PlankBackground(cmbTemp, 1e-3*cmbTemp, 1e3*cmbTemp, 0., Zmax + 1.)); + //IR/O component + backgr.AddComponent(new GaussianBackground(0.1*units.eV, 0.01*units.eV, 5, 1./units.cm3, 0, Zmax + 1.)); + PBackgroundIntegral backgrI(new ContinuousBackgroundIntegral(backgr, stepZ, logStepK, Zmax, epsRel)); + int seed = 2015; + Result result(new EnergyBasedFilter(Emin, M_PI), true); + + SmartPtr<RawOutput> pOutput = new RawOutput(outputDir, false); + result.AddOutput(pOutput); + ParticleStack particles; + PropagationEngine pe(particles, result, seed); + EnergyBasedThinning thinning(alphaThinning); + pe.SetThinning(&thinning); + + Particle proton(Proton, Zmax); + int nSteps = log(Emax/Emin)/log(logStepK)+1.; + double mult = pow(Emax/Emin, 1./nSteps); + { + std::ofstream rateOut; + rateOut.open((outputDir + "/PPP").c_str(),std::ios::out); + PRandomInteraction i = new ProtonPP(backgrI); + pe.AddInteraction(i); + int step=0; + for(proton.Energy=Emax/100; step<=nSteps; proton.Energy*=mult, step++) + rateOut << proton.Energy/units.eV << "\t" << i->Rate(proton)*units.Mpc << "\n"; + rateOut.close(); + } + Randomizer rand; + CosmoTime tEnd; + proton.Energy = Emax; + pOutput->SetOutputDir(outputDir + "/z0"); + result.SetEndTime(tEnd); + + for(int i=0; i<Nparticles; i++) + { + particles.AddPrimary(proton); + } + pe.RunMultithreadReleaseOnly(); +} + +double ProtonPP::SampleE(double aEmin, double aEmax, Randomizer& aRandomizer) const +{//assuming dif sigma is proportional to E^{-7/4} + double r = aRandomizer.Rand(); + double E = pow(pow(aEmin,-0.75)*(1.-r) + pow(aEmax,-0.75)*r, -4./3.); + ASSERT(E>=aEmin && E<=aEmax); + return E; +} + +RandomInteraction* ProtonPP::Clone() const +{ + return new ProtonPP(fBackground->Clone()); +} + +ProtonPPcel::SigmaE::SigmaE() +{ + fInelCoef = 4.*Particle::Mass(Electron)/Particle::Mass(Proton); +} + +double ProtonPPcel::SigmaE::f(double s) const +{ + double k = 0.5*(s - fMp2)*fMpMe_1; // photon energy in p-rest frame in units of electron mass + if(k<=2.) + return 0.; + + //using formulas (3.7)-(3.10) from M. J. Chodorowski, A. A. Zdziarski, and M. Sikora, Astrophys. J. 400, 181 (1992) + double inelasticity = 0.; + if(k<1000.) + { + double ln = log(k-1); + double lnI = 1.; + for(int i=0; i<4; i++, lnI *= ln) + inelasticity += lnI*a[i]; + } + else + { + double ln = log(k); + double lnI = 1.; + for(int i=0; i<4; i++, lnI *= ln) + inelasticity += lnI*b[i]; + inelasticity /= (3.11111111111111*(ln + M_LN2)-8.07407407407407); + } + inelasticity *= (fInelCoef/k); + ASSERT_VALID_NO(inelasticity); + return ProtonPPSigma::sigmaLab(k)*inelasticity; +} + +//constants given by (3.8) from M. J. Chodorowski, A. A. Zdziarski, and M. Sikora, Astrophys. J. 400, 181 (1992) +const double ProtonPPcel::SigmaE::a[] = {1, 0.3958, 0.1, 0.00781}; + +//constants given by (3.10) from M. J. Chodorowski, A. A. Zdziarski, and M. Sikora, Astrophys. J. 400, 181 (1992) +const double ProtonPPcel::SigmaE::b[] = {-8.778, 5.512, -1.614, 0.666666666666667}; + +ProtonPPcel::ProtonPPcel(BackgroundIntegral* aBackground): +fBackground(aBackground) +{ +} + +ProtonPPcel::~ProtonPPcel() { +} + +void ProtonPPcel::UnitTest() +{ + std::ofstream rateOut; + rateOut.open("ppp_test",std::ios::out); + SigmaE sigmaE; + ProtonPPSigma sigma; + double Mp = Particle::Mass(Proton); + double Me = Particle::Mass(Electron); + double coef = 3.88593900935001e-07/Mp/Me; + for(double k = 2.; k<=1e4; k *= 1.58489319246111) + {//compare sigK to dashed curve from figure 2 of M. J. Chodorowski, A. A. Zdziarski, and M. Sikora, Astrophys. J. 400, 181 (1992) + double s = Mp*(Mp + 2.*k*Me); + double sig = sigma(s)/coef; + double sigK = sigmaE(s)/coef; + rateOut << k << "\t" << sig << "\t" << sigK << "\n"; + } + rateOut.close(); +} + +double ProtonPPcel::Rate(const Particle& aParticle) const +{ + if(aParticle.Type != Proton) + return 0.; + + return fBackground->GetRateS(fSigma, aParticle); +} + +void ProtonPPcel::GetSecondaries(const Particle& aParticle, std::vector<Particle>& aSecondaries, + cosmo_time aDeltaT, Randomizer& aRandomizer) const +{ + +} + +CELInteraction* ProtonPPcel::Clone() const +{ + return new ProtonPPcel(fBackground->Clone()); +} + +} /* namespace Interactions */ diff --git a/src/lib/ProtonPP.h b/src/lib/ProtonPP.h new file mode 100644 index 0000000..a1078ec --- /dev/null +++ b/src/lib/ProtonPP.h @@ -0,0 +1,102 @@ +/* + * ProtonPP.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#ifndef PROTONPP_H_ +#define PROTONPP_H_ + +#include "Interaction.h" +#include "GammaPP.h" + +namespace Interactions { + +using namespace mcray; + +class ProtonPPSigma : public Utils::Function, EMInteraction +{ +public: + ProtonPPSigma(); + double f(double s) const; + double sigmaLab(double k) const; + double Xmin() const { return fThresholdS; } +protected: + double fThresholdS; + double fMpMe_1; + double fMp2; + double fCoef; +}; + + /// This is outdated implementation of pair production by protons + /// Use PPP class instead unless you are sure what you are doing +class ProtonPP : public RandomInteractionS{ +public: + ProtonPP(BackgroundIntegral* aBackground); + virtual double Rate(const Particle& aParticle) const; + virtual ~ProtonPP(); + virtual bool SampleS(const Particle& aParticle, double& aS, Randomizer& aRandomizer) const; + virtual void SampleSecondaries(Particle& aParticle, std::vector<Particle>& aSecondaries, double aS, Randomizer& aRandomizer) const; + virtual RandomInteraction* Clone() const; + static void UnitTest(); +private: + double SampleE(double aEmin, double aEmax, Randomizer& aRandomizer) const; + SmartPtr<BackgroundIntegral> fBackground; + ProtonPPSigma fSigma; + static double maxPPPemaxError; +}; + +///Used from PPP class to speed-up simulations +class ProtonPPcel : public CELInteraction +{ + /*! + * 2/E * Integrate[E_e * dSigma/dE_e,{E_e,Emin,Emax}] + * */ + class SigmaE : public ProtonPPSigma + { + public: + SigmaE(); + double f(double s) const; + private: + static const double a[]; + static const double b[]; + double fInelCoef; + }; +public: + static void UnitTest(); + ProtonPPcel(BackgroundIntegral* aBackground); + virtual ~ProtonPPcel(); + double Rate(const Particle& aParticle) const; + virtual void GetSecondaries(const Particle& aParticle, std::vector<Particle>& aSecondaries, + cosmo_time aDeltaT, Randomizer& aRandomizer) const; + + CELInteraction* Clone() const; +private: + SmartPtr<BackgroundIntegral> fBackground; + SigmaE fSigma; +}; + +} /* namespace Interactions */ +#endif /* PROTONPP_H_ */ diff --git a/src/lib/Randomizer.cpp b/src/lib/Randomizer.cpp new file mode 100644 index 0000000..3792adc --- /dev/null +++ b/src/lib/Randomizer.cpp @@ -0,0 +1,97 @@ +/* + * Randomizer.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#include "Randomizer.h" +#include <math.h> +#include "Utils.h" +#include "gsl/gsl_errno.h" +#include "gsl/gsl_math.h" +#include "gsl/gsl_rng.h" + +namespace mcray +{ +Randomizer::Randomizer(unsigned long int aSeed): + fRand2(0) +{ + /* +The following table shows the relative performance of a selection the available random number generators. +The fastest simulation quality generators are taus, gfsr4 and mt19937. +The generators which offer the best mathematically-proven quality are those based on the ranlux algorithm. + + 1754 k ints/sec, 870 k doubles/sec, taus + 1613 k ints/sec, 855 k doubles/sec, gfsr4 + 1370 k ints/sec, 769 k doubles/sec, mt19937 + 565 k ints/sec, 571 k doubles/sec, ranlxs0 + 400 k ints/sec, 405 k doubles/sec, ranlxs1 + 490 k ints/sec, 389 k doubles/sec, mrg + 407 k ints/sec, 297 k doubles/sec, ranlux ---- buggy one, don't use + 243 k ints/sec, 254 k doubles/sec, ranlxd1 + 251 k ints/sec, 253 k doubles/sec, ranlxs2 + 238 k ints/sec, 215 k doubles/sec, cmrg + 247 k ints/sec, 198 k doubles/sec, ranlux389 + 141 k ints/sec, 140 k doubles/sec, ranlxd2 + */ + fRand = gsl_rng_alloc (gsl_rng_ranlxd2);//gsl_rng* + if(aSeed>0)//use aSeed=0 and GSL_RNG_SEED environment variable to overwrite seed at runtime + gsl_rng_set ((gsl_rng*)fRand, aSeed); +} + +Randomizer::~Randomizer() { + gsl_rng_free((gsl_rng*)fRand); + if(fRand2) + gsl_rng_free((gsl_rng*)fRand2); +} + +double Randomizer::GetFreePath(double aMeanFreePath, double& aRand) +{ + if(aRand<0 || aRand>1) + aRand = Rand(); + if(aRand==0) + return 1e300; + return aMeanFreePath * log(1./aRand); +} + +double Randomizer::Rand() +{ + return gsl_rng_uniform_pos ((gsl_rng*)fRand); +} + +unsigned long int Randomizer::CreateIndependent() +{ + if(!fRand2) + { + fRand2 = gsl_rng_alloc (gsl_rng_ranlux389);//must be different type from fRand + unsigned long int max2 = gsl_rng_max ((gsl_rng*)fRand2); + unsigned long int max1 = gsl_rng_max ((gsl_rng*)fRand); + fRand2Max = max1<max2?max1:max2; + gsl_rng_set ((gsl_rng*)fRand2, gsl_rng_uniform_int((gsl_rng*)fRand, fRand2Max)); + } + return gsl_rng_uniform_int((gsl_rng*)fRand2, fRand2Max); +} + +} diff --git a/src/lib/Randomizer.h b/src/lib/Randomizer.h new file mode 100644 index 0000000..69f8ce5 --- /dev/null +++ b/src/lib/Randomizer.h @@ -0,0 +1,58 @@ +/* + * Randomizer.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#ifndef RANDOMIZER_H +#define RANDOMIZER_H + +namespace mcray +{ +///not expected to be thread-safe +class Randomizer { +public: + Randomizer(unsigned long int aSeed=0); + virtual ~Randomizer(); + double Rand();//generate random number in the range [0,1] + + /*! + @param[in] aMeanFreePath mean free path of a particle + @param[in,out] aRand random number used for sampling. If 0<aRand<1 than input value is used for sampling, + otherwise randomly generated number is used and returned as output parameter + @return sampled value of the free path + */ + double GetFreePath(double aMeanFreePath, double& aRand); + + //generate seed for independent Randomizer + unsigned long int CreateIndependent(); +private: + void* fRand; + void* fRand2;//used in CreateIndependent() only + unsigned long int fRand2Max; +}; +} +#endif /* RANDOMIZER_H */ + diff --git a/src/lib/Sophia.cpp b/src/lib/Sophia.cpp new file mode 100644 index 0000000..375e9af --- /dev/null +++ b/src/lib/Sophia.cpp @@ -0,0 +1,180 @@ +/* + * Sophia.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "Sophia.h" + +extern "C" + { + void sample_photopion_(int& aPrimary, double& aEnergyGeV, double& aEpsilonGeV, double& aThetaDeg, int& aNoSecondaries, double aSecEnergies[], int aSecTypes[]); + + void sample_photopion_rel_(int& aPrimary, double& aEpsPrimeGeV, int& aNoSecondaries, double aSecEfrac[], int aSecTypes[]); + + + //initialize SOPHIA + //L0 - primary particle type (13 = proton, 14 = neutron) + void initial_(int& L0); + + //double crossection_(double x, double NDIR, int NL0); + + void crossec_(int& L0, double& eps_prime, double& sigma); + + void get_mass_(int& L0,double& m); + + void set_random_seed_(int& aSeed); + } + +namespace Interactions { + using namespace mcray; + using namespace Utils; + + int SOPHIA::toSOPHIA(ParticleType aType) { + switch (aType) { + case Proton: + return 13; + case Neutron: + return 14; + case Photon: + return 1; + case Positron: + return 2; + case Electron: + return 3; + case NeutrinoE: + return 15; + case NeutrinoAE: + return 16; + case NeutrinoM: + return 17; + case NeutrinoAM: + return 18; + default: + Exception::Throw("unsupported particle type"); + break; + } + return -1; + } + + ParticleType SOPHIA::fromSOPHIA(int aType) { + switch (aType) { + case 1: + return Photon; + case 2: + return Positron; + case 3: + return Electron; + case 13: + return Proton; + case 14: + return Neutron; + case 15: + return NeutrinoE; + case 16: + return NeutrinoAE; + case 17: + return NeutrinoM; + case 18: + return NeutrinoAM; + } + return ParticleTypeEOF; + } + + void SOPHIA::Init(int aPrimary) { + ASSERT(aPrimary == 13 || aPrimary == 14); + if (aPrimary != LastInit) { + initial_(aPrimary); + LastInit = aPrimary; + } + } + + void SOPHIA::SamplePhotopion(ParticleType aPrimary, double aEnergyGeV, double aEpsilonGeV, double aThetaDeg, + int &aNoSecondaries, double aSecEnergies[], int aSecTypes[]) { + ASSERT(aPrimary == Proton || aPrimary == Neutron); + int primary = toSOPHIA(aPrimary); + Init(primary); + sample_photopion_(primary, aEnergyGeV, aEpsilonGeV, aThetaDeg, aNoSecondaries, aSecEnergies, aSecTypes); + for (int i = 0; i < aNoSecondaries; i++) + aSecTypes[i] = fromSOPHIA(aSecTypes[i]); + } + + double SOPHIA::CrossSection(int aPrimary, double aEpsPrimeGeV) { + Init(aPrimary); + double result = 0; + crossec_(aPrimary, aEpsPrimeGeV, result); + return result; + } + + const int SOPHIA::MaxNumberOfProducts = 2000; + int SOPHIA::LastInit = -1; + + SophiaCS::SophiaCS(ParticleType aPrimary) : + fPrimary(SOPHIA::toSOPHIA(aPrimary)) { + const double Smin = 1.1646; + ASSERT(fPrimary == 13 || fPrimary == 14); + double pm = fPrimary == 13 ? 0.93827 : 0.93957; + fXmin = 0.5 * (Smin / pm - pm); + } + + double SophiaCS::f(double _x) const { + return SOPHIA::CrossSection(fPrimary, _x); + } + + double SophiaCS::Xmin() const { + return fXmin; + } + + void SOPHIA::SamplePhotopionRel(mcray::ParticleType aPrimary, double aEpsPrimeGeV, int &aNoSecondaries, + double aSecEfrac[], int aSecTypes[]) { + ASSERT(aPrimary == Proton || aPrimary == Neutron); + int primary = toSOPHIA(aPrimary); + Init(primary); + sample_photopion_rel_(primary, aEpsPrimeGeV, aNoSecondaries, aSecEfrac, aSecTypes); + for (int i = 0; i < aNoSecondaries; i++) + aSecTypes[i] = fromSOPHIA(aSecTypes[i]); + } + + bool SOPHIA::RandomSeedSet = false; + + void SOPHIA::SetRandomSeed(int aSeed) { + bool doJob = true; +#pragma omp critical (SOPHIA) + { + if (RandomSeedSet) + doJob = false;//initialize only once + else + RandomSeedSet = true; + } + if(doJob) + set_random_seed_(aSeed); + } + + double SOPHIA::Mass(mcray::ParticleType aParticle) { + int particle = toSOPHIA(aParticle); + double mGeV = 0.; + get_mass_(particle, mGeV); + return mGeV * units.GeV; + } +} diff --git a/src/lib/Sophia.h b/src/lib/Sophia.h new file mode 100644 index 0000000..f558c85 --- /dev/null +++ b/src/lib/Sophia.h @@ -0,0 +1,82 @@ +/* + * Sophia.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef SOPHIA_H_ +#define SOPHIA_H_ + +#include "Particle.h" +#include "MathUtils.h" + +namespace Interactions { + using namespace mcray; + +///stub for calls to SOPHIA +///the class is not thread-safe + class SOPHIA { + static int LastInit; + static bool RandomSeedSet; + + static void Init(int aPrimary); + + public: + static const int MaxNumberOfProducts; + + static int toSOPHIA(mcray::ParticleType aType); + + static mcray::ParticleType fromSOPHIA(int aType); + + //this method is not thread-safe!!! + static void SamplePhotopion(mcray::ParticleType aPrimary, double aEnergyGeV, double aEpsilonGeV, + double aThetaDeg, int &aNoSecondaries, double aSecEnergies[], int aSecTypes[]); + + //this method is not thread-safe!!! + //sample energies of secondaries in units of primary energy in ultrarelativistic limit + static void SamplePhotopionRel(mcray::ParticleType aPrimary, double aEpsPrimeGeV, int &aNoSecondaries, + double aSecEfrac[], int aSecTypes[]); + + //this method is not thread-safe + static double CrossSection(int aPrimary, double aEpsPrimeGeV); + + static double Mass(mcray::ParticleType aParticle); + + static void SetRandomSeed(int aSeed); + }; + + class SophiaCS : public Utils::Function { + public: + SophiaCS(mcray::ParticleType aPrimary); + + virtual double f(double _x) const; + + virtual double Xmin() const; + + private: + double fXmin; + int fPrimary; + }; +} +#endif /* SOPHIA_H_ */ diff --git a/src/lib/Stecker16Background.cpp b/src/lib/Stecker16Background.cpp new file mode 100644 index 0000000..6f27e15 --- /dev/null +++ b/src/lib/Stecker16Background.cpp @@ -0,0 +1,60 @@ +/* + * Stecker16Background.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#include "Stecker16Background.h" +#include "Units.h" + +namespace Backgrounds { + + Stecker16LowerBackground::Stecker16LowerBackground() : + Stecker16Background(TABLES_DIR "stecker_ebl16/comoving_enerdens_lo.csv", "Stecker_16_lower") { +} + +Stecker16UpperBackground::Stecker16UpperBackground() : + Stecker16Background(TABLES_DIR "stecker_ebl16/comoving_enerdens_up.csv", "Stecker_16_upper") +{ +} + + +double Stecker16Background::n(double E, double z) const { + // matrix function returns the spectrum E*dn/dE in sm^-3 [E]=eV in comoving volume + double result = fMatrixFunction.f(z, E/units.eV) / units.Hz_photon * units.erg; + double dl = (1.+z); + //convert to physical density in internal units + return dl*dl*dl/units.cm3*result; +} + +int Stecker16Background::UnitTest() +{ + Stecker16UpperBackground backgrUpper; + BackgroundUtils::UnitTest(backgrUpper); + Stecker16LowerBackground backgrLower; + BackgroundUtils::UnitTest(backgrLower); +} + +} \ No newline at end of file diff --git a/src/lib/Stecker16Background.h b/src/lib/Stecker16Background.h new file mode 100644 index 0000000..b996e01 --- /dev/null +++ b/src/lib/Stecker16Background.h @@ -0,0 +1,82 @@ +/* + * Stecker16Background.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#ifndef PROPAGATION_STECKER16BACKGROUND_H +#define PROPAGATION_STECKER16BACKGROUND_H + +#include "Background.h" +//#include "Vector.h" +//#include "TableFunc.h" +#include <string> +#include <vector> +//#include "DataReader.h" +using namespace std; + +#include "Background.h" + +namespace Backgrounds { + + using namespace mcray; + + class Stecker16Background : public IBackground { + public: + Stecker16Background(string aTableFile, std::string aName) : + fMatrixFunction(aTableFile), + fName(aName) + {} + + static int UnitTest(); +/*! + Calculate density E*dn(E)/dE at given redshift in internal units. Physical density should be returned, not a comoving one + @param[in] aE background photon energy (in internal units) + @param[in] aZ red shift + @return background photon density E*dn(E)/dE in internal units +*/ + virtual double n(double aE, double aZ) const; + virtual double MinE() const { return fMatrixFunction.MinArg(2)*units.eV; } + virtual double MaxE() const { return fMatrixFunction.MaxArg(2)*units.eV; } + virtual double MinZ() const { return fMatrixFunction.MinArg(1); } + virtual double MaxZ() const { return fMatrixFunction.MaxArg(1); } + virtual std::string Name() const { return fName; }; + + private: + MatrixFunction fMatrixFunction; + std::string fName; + }; + + class Stecker16LowerBackground : public Stecker16Background { + public: + Stecker16LowerBackground(); + }; + + class Stecker16UpperBackground : public Stecker16Background { + public: + Stecker16UpperBackground(); + }; +} +#endif //PROPAGATION_STECKER16BACKGROUND_H diff --git a/src/lib/SteckerEBL.cpp b/src/lib/SteckerEBL.cpp new file mode 100644 index 0000000..4064ead --- /dev/null +++ b/src/lib/SteckerEBL.cpp @@ -0,0 +1,166 @@ +/* + * SteckerEBL.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#include <algorithm> +#include "SteckerEBL.h" +#include "TableBackgrounds.h" + +using namespace mcray; + +namespace Backgrounds { + +#define IRO_DATA_FILE "iro_stecker2005" + Stecker2005EBL::Stecker2005EBL(): + CTableWithHeaderReader(TABLES_DIR IRO_DATA_FILE), + m_accuracyE(0), + m_accuracyZ(0), + m_curE(0) +{ + read(); +} + +Stecker2005EBL::~Stecker2005EBL() +{ + +} + +double Stecker2005EBL::n(double aE, double aZ) const +{ + aE /= units.eV;//convert internal units to eV + int iZ=0; + // int iE=-1; + if(aZ < 1e-4)//z=0 + { + return m_fE[0]->f(aE);// logarithmic approximation on E + } + //iZ=m_zArray.findLeftX(z); + //std::vector<double>::iterator it=std::find_if(m_zArray.begin(),m_zArray.end(),[](double x){return (x > z);}); + iZ=TableFunction::FindLeftX(m_zArray, aZ); + + if(iZ<0) + return 0.; + + double n1 = m_fE[iZ]->f(aE);// logarithmic approximation on E + double n2 = m_fE[iZ+1]->f(aE); + double z1 = m_zArray[iZ]; + double z2 = m_zArray[iZ+1]; + + double result = n1 + (n2-n1)/(z2-z1)*(aZ - z1); // simple linear approximation on z + + return result; +} + +std::string Stecker2005EBL::Name() const +{ + return "Stecker05"; +} + +double Stecker2005EBL::MinE() const +{ + return m_eArray[0]*units.eV; +} + +double Stecker2005EBL::MaxE() const +{ + return m_eArray[m_accuracyE-1]*units.eV; +} + +double Stecker2005EBL::MaxZ() const +{ + return m_zArray[m_accuracyZ-1]; +} + +double Stecker2005EBL::MinZ() const +{ + return 0; +} + +CTableWithHeaderReader::ECompleteStatus Stecker2005EBL::readHeaderLine(const char* theString) +{ + if (m_accuracyE<=0) + { + return readDataLength(m_accuracyE,theString)?notCompletedE:failedE; + } + + if (!readVector(m_zArray, theString)) + { + return failedE; + } + + m_eArray.insert(m_eArray.begin(),m_accuracyE,0.0); + m_accuracyZ = m_zArray.size(); + std::vector<double> v; + m_nArray.insert(m_nArray.begin(),m_accuracyZ,v); + for(size_t i=0; i<m_accuracyZ; i++) + m_nArray[i].insert(m_nArray[i].begin(), m_accuracyE, 0.0); + + SafePtr<TableFunction> ptr; + m_fE.insert(m_fE.begin(),m_accuracyZ, ptr); + + return completedE; +} + +bool Stecker2005EBL::readDataLine(const char* theString) +{ + if (m_curE>=m_accuracyE) + { + return false; + } + std::vector<double> v; + if(!readVector(v, theString)) + return false; + ASSERT(v.size()==m_accuracyZ+1); + double E = pow(10.,v[0]-9.)/241803.; // first column contains log10(E/Hz), converting to eV + m_eArray[m_curE] = E; + for(int i=0; i< m_accuracyZ; i++) + //m_nArray[i][m_curE] = v[i+1]*1e-6*E*pow(1.+m_zArray[i],-3); // multiply by energy and convert m^-3 to cm^-3, multiplied by (1+z)^{-3} to make it in comoving volume + m_nArray[i][m_curE] = v[i+1]*1e-6/units.cm3; // convert from m^-3 to internal units in physical volume + m_curE++; + return true; +} + +bool Stecker2005EBL::testData() +{ + return true; +} + +void Stecker2005EBL::processData() +{ + for(int i=0; i<m_accuracyZ; i++) + m_fE[i] = new LinearFunc(m_eArray, m_nArray[i]); +} + +int Stecker2005EBL::UnitTest() +{ + Stecker2005EBL backgr; + //Kneiske1001EBL backgr; + //CuttedBackground backgr(new Stecker2005EBL(), 0, 0.5*units.eV); + BackgroundUtils::UnitTest(backgr); +} + +} \ No newline at end of file diff --git a/src/lib/SteckerEBL.h b/src/lib/SteckerEBL.h new file mode 100644 index 0000000..8d00a02 --- /dev/null +++ b/src/lib/SteckerEBL.h @@ -0,0 +1,75 @@ +/* + * SteckerEBL.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#ifndef MCRAY_STECKEREBL_H +#define MCRAY_STECKEREBL_H + +#include "Background.h" + +namespace Backgrounds { + using namespace mcray; + + +// IR/O spectrum from Stecker at al. astro-ph/0510449 +//(received from Matt Malkan <malkan@astro.UCLA.EDU> on the 6th of Jan 2006) + + class Stecker2005EBL : public CTableWithHeaderReader, public IBackground + { + public: + Stecker2005EBL(); + virtual ~Stecker2005EBL(); + + + virtual double n(double aE, double aZ) const; + virtual double MinE() const; + virtual double MaxE() const; + virtual double MinZ() const; + virtual double MaxZ() const; + virtual std::string Name() const; + + static int UnitTest(); + + private: +// from CDataReader + ECompleteStatus readHeaderLine(const char* theString); + bool readDataLine(const char* theString); + bool testData(); + void processData(); + +// data + int m_accuracyE; + int m_accuracyZ; + int m_curE; + std::vector<double> m_zArray;// redshifts + std::vector<double> m_eArray;// energies + std::vector<std::vector<double> > m_nArray;// photon concentrations + std::vector<SafePtr<TableFunction> > m_fE; /// F(E,z_i) + }; +} + +#endif //MCRAY_STECKEREBL_H diff --git a/src/lib/TableBackgrounds.cpp b/src/lib/TableBackgrounds.cpp new file mode 100644 index 0000000..af2a48e --- /dev/null +++ b/src/lib/TableBackgrounds.cpp @@ -0,0 +1,184 @@ +/* + * TableBackgrounds.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "TableBackgrounds.h" +#include "Units.h" +#include <math.h> + +namespace Backgrounds { + + +Kneiske1001EBL::Kneiske1001EBL(): + TableBackground("Kneiske1001 lower-limit") +{ + const char* files[] = {"0","0.1","0.3","0.8","2",0}; + Init("Kneiske1001", files, true); +} + +Kneiske1001EBL::~Kneiske1001EBL() { +} + +//convert energy in internal units to table x scale +double Kneiske1001EBL::EtoRecordX(double aE, double aZ) const +{ + double lambda = 2.*M_PI/aE;//wavelength in internal units + lambda *= units.Lunit*1e4;//wavelength in microns + return log10(lambda); +} + +//retrieve energy in internal units from the data record +double Kneiske1001EBL::recordToE(double aX, double aZ) const +{ + double lambda = pow(10.,aX);//wavelength in microns + lambda /= (units.Lunit*1e4);//wavelength in internal units + return 2.*M_PI/lambda; +} + +////retrieve concentration E*dn/dE in internal units from the data record +double Kneiske1001EBL::recordToN(double aX, double aY, double aZ) const +{//[y] = nW m^-2 sr^-1 in comoving frame + double comovingFactor = (1.+aZ); + comovingFactor *= (comovingFactor*comovingFactor); + double aE = recordToE(aX, aZ); + double y = pow(10.,aY); + y *= (4.*M_PI*1e-9/(units.MeVinESU*1e-7/units.Eunit)*1e-4*units.Lunit*units.Lunit*units.Tunit); + double result = y/aE*comovingFactor; + return result; +} + +Kneiske0309EBL::Kneiske0309EBL(): + TableBackground("Kneiske0309 best fit") +{ + const char* files[] = {"0","0.2","0.4","0.6","1","2","3","4",0}; + Init("Kneiske0309", files, true); +} + +Kneiske0309EBL::~Kneiske0309EBL() { +} + +//convert energy in internal units to table x scale +double Kneiske0309EBL::EtoRecordX(double aE, double aZ) const +{ + double lambda = 2.*M_PI/aE;//wavelength in internal units + lambda *= units.Lunit*1e4;//wavelength in microns + return log10(lambda); +} + +//retrieve energy in internal units from the data record +double Kneiske0309EBL::recordToE(double aX, double aZ) const +{ + double lambda = pow(10.,aX);//wavelength in microns + lambda /= (units.Lunit*1e4);//wavelength in internal units + return 2.*M_PI/lambda; +} + +////retrieve concentration E*dn/dE in internal units from the data record +double Kneiske0309EBL::recordToN(double aX, double aY, double aZ) const +{//[y] = nW m^-2 sr^-1 in comoving frame + double comovingFactor = (1.+aZ); + comovingFactor *= (comovingFactor*comovingFactor); + double aE = recordToE(aX, aZ); + double y = pow(10.,aY); + y *= (4.*M_PI*1e-9/(units.MeVinESU*1e-7/units.Eunit)*1e-4*units.Lunit*units.Lunit*units.Tunit); + double result = y/aE*comovingFactor; + return result; +} + +////////// + +Franceschini08EBL::Franceschini08EBL(): + TableBackground("Franceschini 08") +{ + const char* files[] = {"0","0.2","0.4","0.6","0.8","1.0","1.2","1.4","1.6","1.8","2.0", 0}; + Init("Franceschini08EBL", files, true); +} + +Franceschini08EBL::~Franceschini08EBL() { +} + +//convert energy in internal units to table x scale +double Franceschini08EBL::EtoRecordX(double aE, double aZ) const +{ + return log10(aE/units.eV); +} + +//retrieve energy in internal units from the data record +double Franceschini08EBL::recordToE(double aX, double aZ) const +{ + return pow(10.,aX)*units.eV; +} + +////retrieve concentration E*dn/dE in internal units from the data record +double Franceschini08EBL::recordToN(double aX, double aY, double aZ) const +{//[y] = log(E*dn/dE) in cm^-3 in physical frame + double y = pow(10.,aY); + return y/units.cm3; +} + +//////////// + +ElmagKneiskeBestFit::ElmagKneiskeBestFit(bool aIsComoving): + MatrixBackground("Elmag best fit", "kneiskeElmagBestFit.dat", aIsComoving, true) +{ +} + +ElmagKneiskeMinimal::ElmagKneiskeMinimal(bool aIsComoving): + MatrixBackground("Elmag lower-limit", "kneiskeElmagLowerLimit.dat", aIsComoving, true) +{ +} + +/////////////// +Franceschini17EBL::Franceschini17EBL(): + TableBackground("Franceschini17") +{ + const char* files[] = {"0", "0.2", "0.4", "0.6", "0.8", "1", "1.2", "1.4", "1.8", "2", 0}; + Init("Franceschini17", files, true); +} + +Franceschini17EBL::~Franceschini17EBL() { +} + +//convert energy in internal units to table x scale +double Franceschini17EBL::EtoRecordX(double aE, double aZ) const +{ + return log10(aE/units.eV); +} + +//retrieve energy in internal units from the data record +double Franceschini17EBL::recordToE(double aX, double aZ) const +{ + return pow(10.,aX)*units.eV; +} + +////retrieve concentration E*dn/dE in internal units from the data record +double Franceschini17EBL::recordToN(double aX, double aY, double aZ) const +{ + return pow(10., aY)/units.cm3; +} + +} /* namespace Backgrounds */ + diff --git a/src/lib/TableBackgrounds.h b/src/lib/TableBackgrounds.h new file mode 100644 index 0000000..4db8215 --- /dev/null +++ b/src/lib/TableBackgrounds.h @@ -0,0 +1,115 @@ +/* + * TableBackgrounds.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#ifndef TABLEBACKGROUNDS_H_ +#define TABLEBACKGROUNDS_H_ + +#include "Background.h" + +namespace Backgrounds { + +using namespace mcray; + +class Kneiske1001EBL : public TableBackground{ +public: + Kneiske1001EBL(); + virtual ~Kneiske1001EBL(); +protected: + //convert energy in internal units to table x scale + double EtoRecordX(double aE, double aZ) const; + //retrieve energy in internal units from the data record + double recordToE(double aX, double aZ) const; + ////retrieve concentration E*dn/dE in internal units from the data record + double recordToN(double aX, double aY, double aZ) const; +}; + +class Kneiske0309EBL : public TableBackground{ +public: + Kneiske0309EBL(); + virtual ~Kneiske0309EBL(); +protected: + //convert energy in internal units to table x scale + double EtoRecordX(double aE, double aZ) const; + //retrieve energy in internal units from the data record + double recordToE(double aX, double aZ) const; + ////retrieve concentration E*dn/dE in internal units from the data record + double recordToN(double aX, double aY, double aZ) const; +}; + +/// EBL from Table1,2 of http://arxiv.org/pdf/0805.1841v2.pdf +class Franceschini08EBL : public TableBackground{ +public: + Franceschini08EBL(); + virtual ~Franceschini08EBL(); +protected: + //convert energy in internal units to table x scale + double EtoRecordX(double aE, double aZ) const; + //retrieve energy in internal units from the data record + double recordToE(double aX, double aZ) const; + ////retrieve concentration E*dn/dE in internal units from the data record + double recordToN(double aX, double aY, double aZ) const; +}; + +class ElmagKneiskeBestFit : public MatrixBackground +{ +public: + /// Kneiske BestFit Background used in Elmag + /// There is an error in Elmag 1.02 and earlier, the tables are treated as physical concentrations + /// But in fact they are built for comoving frame (see fig 1 of http://lanl.arxiv.org/abs/astro-ph/0309141v1) + /// The parameter here is left just for comparison to Elmag 1.02 + /// In Elmag 2.0 this bug was fixed + ElmagKneiskeBestFit(bool aIsComoving = true); +}; + +class ElmagKneiskeMinimal : public MatrixBackground +{ +public: + /// Kneiske Lower limit Background used in Elmag + /// There is an error in Elmag 1.02 and earlier, the tables are treated as physical concentrations + /// But in fact they are built for comoving frame + /// The parameter here is left just for comparison to Elmag 1.02 + /// In Elmag 2.0 this bug was fixed + ElmagKneiskeMinimal(bool aIsComoving = true); +}; + +class Franceschini17EBL : public TableBackground{ +public: + Franceschini17EBL(); + virtual ~Franceschini17EBL(); +protected: + //convert energy in internal units to table x scale + double EtoRecordX(double aE, double aZ) const; + //retrieve energy in internal units from the data record + double recordToE(double aX, double aZ) const; + ////retrieve concentration E*dn/dE in internal units from the data record + double recordToN(double aX, double aY, double aZ) const; +}; + +} /* namespace Backgrounds */ + +#endif /* TABLEBACKGROUNDS_H_ */ diff --git a/src/lib/TableFunction.cpp b/src/lib/TableFunction.cpp new file mode 100644 index 0000000..dc266a8 --- /dev/null +++ b/src/lib/TableFunction.cpp @@ -0,0 +1,256 @@ +/* + * TableFunction.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#include "PrecisionTests.h" +#include "TableFunction.h" + +namespace Utils { + + +template<typename X> X LogScaleX<X>::ToScale(X aX) +{ + ASSERT(aX>=0); + return aX>0?log(aX):-1000; +} + +template<typename X> X LogScaleX<X>::FromScale(X aXprime) +{ + return exp(aXprime); +} + +template<typename X> IScale<X>* LogScaleX<X>::Clone() const +{ + return new LogScaleX(); +} + +template class LogScaleX<double>; +template class LogScaleX<long double>; + +template<typename X> void TableFunctionX<X>::Init() +{ + ASSERT(m_x.size()==m_y.size() && m_x.size()>1); + fXmin = m_x[0]; + fXmax = m_x[m_x.size()-1]; +} + +template<typename X> TableFunctionX<X>::TableFunctionX(const std::vector<X>& _x, const std::vector<X>& _y, X _leftVal, X _rightVal): +m_x(_x), +m_y(_y), +m_leftVal(_leftVal), +m_rightVal(_rightVal) +{ + Init(); +} + +template<typename X> TableFunctionX<X>::TableFunctionX(const std::vector<X>& _x, const std::vector<X>& _y, IScale<X>* aXscale, IScale<X>* aYscale, X _leftVal, X _rightVal): +fCloneX(_x), +fCloneY(_y), +m_x(fCloneX), +m_y(fCloneY), +m_leftVal(_leftVal), +m_rightVal(_rightVal), +fXscale(aXscale), +fYscale(aYscale) +{ + Init(); + int iMax = _x.size(); + for(int i=0; i<iMax; i++) + { + if(aXscale) + fCloneX[i] = aXscale->ToScale(_x[i]); + if(aYscale) + fCloneY[i] = aYscale->ToScale(_y[i]); + } +} + +template<typename X> TableFunctionX<X>::TableFunctionX(TableReaderX<X>* aReader, X _leftVal, X _rightVal): + m_x(aReader->getColumn(0)), + m_y(aReader->getColumn(1)), + m_leftVal(_leftVal), + m_rightVal(_rightVal), + fTable(aReader) +{ + Init(); +} + + +template<typename X> TableFunctionX<X>::TableFunctionX(const TableFunctionX<X>& aTableFunction): +fCloneX(aTableFunction.m_x), +fCloneY(aTableFunction.m_y), +m_x(fCloneX), +m_y(fCloneY), +m_leftVal(aTableFunction.m_leftVal), +m_rightVal(aTableFunction.m_rightVal), +fXscale(aTableFunction.fXscale==0 ? 0 : aTableFunction.fXscale->Clone()), +fYscale(aTableFunction.fYscale==0 ? 0 : aTableFunction.fYscale->Clone()), +fXmin(aTableFunction.fXmin), +fXmax(aTableFunction.fXmax) +{ + +} + +template<typename X> int TableFunctionX<X>::FindLeftX(const std::vector<X>&aSet, X xValue) +{ + int right = aSet.size() - 1; + ASSERT(right>=1); + if((xValue > aSet[right]) || xValue < aSet[0]) + return -1; + int left = 0; + + for(int i=(left+right)/2;right-left>1;i=(left+right)/2) + { + if(xValue > aSet[i]) + left=i; + else + right=i; + } + ASSERT((right - left) == 1); + return left; +} + +template<typename X> int TableFunctionX<X>::FindLeftX(X xValue) const +{ + return FindLeftX(m_x, xValue); +} + +template<typename X> X TableFunctionX<X>::f(X _x) const +{ + if(_x<fXmin) + return m_leftVal; + if(_x>fXmax) + return m_rightVal; + X scaledX = fXscale ? fXscale->ToScale(_x) : _x; + X scaledY = f_scaled(scaledX); + return fYscale ? fYscale->FromScale(scaledY) : scaledY; +} + +template<typename X> void TableFunctionX<X>::SetAutoLimits() +{ + m_leftVal = fYscale ? fYscale->FromScale(m_y[0]) : m_y[0]; + m_rightVal = fYscale ? fYscale->FromScale(m_y[m_y.size()-1]) : m_y[m_y.size()-1]; +} + +template<typename X> X TableFunctionX<X>::Xmin() const +{ + return m_leftVal == 0. ? fXmin : -DBL_MAX; +} + +template<typename X> X TableFunctionX<X>::Xmax() const +{ + return m_rightVal == 0. ? fXmax : DBL_MAX; +} + +template<typename X> bool TableFunctionX<X>::InTableRange(X aArg) const +{ + return aArg>=fXmin && aArg<=fXmax; +} + +template<typename X> void TableFunctionX<X>::Print(std::ostream& aOut, X aXcoef, X aYcoef) +{ + int iMax = m_x.size(); + aOut << "# " << "leftVal=" << aYcoef*m_leftVal << " ; rightVal=" << aYcoef*m_rightVal << " ; Xmin=" << aXcoef*Xmin() << " ; Xmax=" << aXcoef*Xmax() << "\n"; + for(int i=0; i<iMax; i++) + { + X xScaled = m_x[i]; + X yScaled = m_y[i]; + X x = fXscale ? fXscale->FromScale(xScaled) : xScaled; + X y = fYscale ? fYscale->FromScale(yScaled) : yScaled; + aOut << aXcoef*x << "\t" << aYcoef*y << "\t" << xScaled << "\t" << yScaled << "\n"; + } +} + +template class TableFunctionX<double>; +template class TableFunctionX<long double>; + +template<typename X> X LinearFuncX<X>::f_scaled(X _x) const +{ + int i = TableFunctionX<X>::FindLeftX(_x); + + if(i<0) + return (_x < this->m_x[0])?this->m_leftVal:this->m_rightVal; + + X x1 = this->m_x[i]; + X y1 = this->m_y[i]; + X y2 = this->m_y[i+1]; + X x2 = this->m_x[i+1]; + + return y1+(y2-y1)/(x2-x1)*(_x - x1); +} + +template class LinearFuncX<double>; +template class LinearFuncX<long double>; + +GSLTableFunc::~GSLTableFunc() +{ + if(fSpline) + gsl_spline_free (fSpline); + if(fAcc) + gsl_interp_accel_free (fAcc); +} + +double GSLTableFunc::f_scaled(double _x) const +{ + return gsl_spline_eval (fSpline, _x, fAcc); +} + +void GSLTableFunc::Init(const gsl_interp_type * aInterpType) +{ + fAcc = gsl_interp_accel_alloc (); + fSpline = gsl_spline_alloc (aInterpType, m_x.size()); + try{ + gsl_spline_init (fSpline, &m_x[0], &m_y[0], m_x.size()); + } + catch(Exception* ex) + { + std::cerr << "GSLTableFunc::Init() error\n input data:\n"; + Print(std::cerr); + throw ex; + } +} + + + +#ifdef USE_MPFR + +template<> mpfr::mpreal LogScaleX<mpfr::mpreal>::FromScale(mpfr::mpreal aXprime) +{ + return mpfr::exp(aXprime); +} + +template<> mpfr::mpreal LogScaleX<mpfr::mpreal>::ToScale(mpfr::mpreal aX) +{ + ASSERT(aX>=0); + return aX>0?mpfr::log(aX):-1000; +} + +template class TableFunctionX<mpfr::mpreal>; +template class LinearFuncX<mpfr::mpreal>; + +#endif + +} /* namespace Utils */ diff --git a/src/lib/TableFunction.h b/src/lib/TableFunction.h new file mode 100644 index 0000000..f29c7d3 --- /dev/null +++ b/src/lib/TableFunction.h @@ -0,0 +1,261 @@ +/* + * TableFunction.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#ifndef TABLEFUNCTION_H_ +#define TABLEFUNCTION_H_ + +#include <iostream> +#include <gsl/gsl_errno.h> +#include <gsl/gsl_spline.h> + +#include "MathUtils.h" +#include "TableReader.h" + + +namespace Utils { + +template<typename X = double > +class IScale +{ +public: + virtual X ToScale(X aX) = 0; + virtual X FromScale(X aXprime) = 0; + virtual IScale<X>* Clone() const = 0; + virtual ~IScale(){} +}; + +template<typename X = double > +class LogScaleX : public IScale<X> +{ +public: + X ToScale(X aX); + X FromScale(X aXprime); + IScale<X>* Clone() const; +}; + +typedef LogScaleX<double> LogScale; + +template<typename X = double > +class TableFunctionX : public SmartReferencedObj, virtual public FunctionX<X>{ +public: + static int FindLeftX(const std::vector<X>&, X xValue); + TableFunctionX(const std::vector<X>& _x, const std::vector<X>& _y, X _leftVal=0., X _rightVal=0.); + + //takes ownership of aXscale and aYscale + TableFunctionX(const std::vector<X>& _x, const std::vector<X>& _y, IScale<X>* aXscale, IScale<X>* aYscale, X _leftVal=0., X _rightVal=0.); + + //stores smart ref to aReader + TableFunctionX(TableReaderX<X>* aReader, X _leftVal=0., X _rightVal=0.); + + bool InTableRange(X aArg) const; + + virtual X f(X _x) const; + virtual X f_scaled(X _x) const = 0; + virtual X Xmin() const; + virtual X Xmax() const; + + void SetAutoLimits(); + void Print(std::ostream& aOut, X aXcoef=1., X aYcoef=1.); +private: + //common part of constructors + void Init(); +protected: + int FindLeftX(X xValue) const; + //used for cloning + TableFunctionX(const TableFunctionX<X>& aTableFunction); + + std::vector<X> fCloneX; + std::vector<X> fCloneY; + const std::vector<X>& m_x; + const std::vector<X>& m_y; + X m_leftVal; + X m_rightVal; + SafePtr<IScale<X> > fXscale; + SafePtr<IScale<X> > fYscale; + SmartPtr<TableReaderX<X> > fTable; + X fXmin; + X fXmax; +}; + +typedef TableFunctionX<double> TableFunction; + +template<typename X = double > +class LinearFuncX : public TableFunctionX<X> +{ +public: + LinearFuncX(const std::vector<X>& _x,const std::vector<X>& _y, X _leftVal=0., X _rightVal=0.): + TableFunctionX<X>(_x, _y, _leftVal, _rightVal) {}; + + //stores smart ref to aReader + LinearFuncX(TableReaderX<X>* aReader, X _leftVal=0., X _rightVal=0.): + TableFunctionX<X>(aReader,_leftVal,_rightVal) {}; + + //takes ownership of aXscale and aYscale + LinearFuncX(const std::vector<X>& _x,const std::vector<X>& _y, IScale<X>* aXscale, IScale<X>* aYscale, X _leftVal=0., X _rightVal=0.): + TableFunctionX<X>(_x, _y, aXscale, aYscale, _leftVal, _rightVal) {} + + X f_scaled(X _x) const; + FunctionX<X>* Clone() const { return new LinearFuncX<X>(*this); } +protected: + LinearFuncX(const TableFunctionX<X>& aTableFunction): + TableFunctionX<X>(aTableFunction) {}; +}; + +typedef LinearFuncX<double> LinearFunc; + +class GSLTableFunc : public TableFunction +{ +public: + GSLTableFunc(const Vector& _x, const Vector& _y, const gsl_interp_type * aInterpType=gsl_interp_linear, + double _leftVal=0., double _rightVal=0.): + TableFunction(_x, _y, _leftVal, _rightVal),fAcc(0),fSpline(0) {Init(aInterpType);} + + //stores smart ref to aReader + GSLTableFunc(TableReader *aReader, const gsl_interp_type * aInterpType=gsl_interp_linear, double _leftVal=0., double _rightVal=0.): + TableFunction(aReader,_leftVal,_rightVal),fAcc(0),fSpline(0) {Init(aInterpType);} + + //takes ownership of aXscale and aYscale + //_x and _y are values are modified with scale functions given by aXscale and aYscale parameters + GSLTableFunc(const Vector& _x, const Vector& _y, IScale<double>* aXscale, IScale<double>* aYscale, const gsl_interp_type * aInterpType=gsl_interp_linear, double _leftVal=0., double _rightVal=0.): + TableFunction(_x, _y, aXscale, aYscale, _leftVal, _rightVal) {Init(aInterpType);} + + virtual ~GSLTableFunc(); + + double f_scaled(double _x) const; + Function* Clone() const { return new GSLTableFunc(*this); } +protected: + GSLTableFunc(const GSLTableFunc& aTableFunction): + TableFunction(aTableFunction) {Init(aTableFunction.fSpline->interp->type);}; +private: + void Init(const gsl_interp_type * aInterpType); + gsl_interp_accel *fAcc; + gsl_spline *fSpline; + const gsl_interp_type *fInterpType; +}; + +template<typename X = double > +class MatrixFunctionX : public Function2X<X> +{ +public: + MatrixFunctionX(const std::string& aFile) + { + std::ifstream dataFile(aFile.c_str()); + if(!dataFile) + Exception::Throw("Failed to open " + aFile); + std::string header; + std::getline(dataFile, header); + int logscX,logscY; + const char* headerFormat = "# minX=%lg stepX=%lg logscaleX=%d minY=%lg stepY=%lg logscaleY=%d"; + if(sscanf(header.c_str(),headerFormat, + &xMin,&xStep,&logscX,&yMin,&yStep,&logscY)!=6) + Exception::Throw("Invalid header format in " + aFile + "\nExpected format: " + headerFormat); + logScaleX = (bool)logscX; + logScaleY = (bool)logscY; + if((logScaleX && (xMin<=0 || xStep<=1.))||xStep<=0.) + Exception::Throw("Invalid X scale in " + aFile); + if((logScaleY && (yMin<=0 || yStep<=1.))||yStep<=0.) + Exception::Throw("Invalid Y scale in " + aFile); + fData = new TableReaderX<X>(dataFile, TableReader::Auto); + nCols = fData->numberOfColumns(); + if(logScaleX) { + xMax = xMin*pow(xStep,nCols-1); + xStep = log(xStep);//convert multiplier to log step + } + else{ + xMax = xMin + xStep*(nCols-1); + } + nRows = fData->getColumn(0).size(); + if(logScaleY){ + yMax = yMin*pow(yStep,nRows-1); + yStep = log(yStep);//convert multiplier to log step + } + else{ + yMax = yMin + yStep*(nRows-1); + } + } + + X f(double x, double y) const + { + double binX, binY; + + if(logScaleX) + {//log scale + binX = log(x/xMin)/xStep; + } + else + {//linear scale + binX = (x-xMin)/xStep; + } + if(binX<0. || binX>nCols-1) + return 0.; + if(logScaleY) + {//log scale + binY = log(y/yMin)/yStep; + } + else + {//linear scale + binY = (y-yMin)/yStep; + } + if(binY<0. || binY>nRows-1) + return 0.; + int iX = floor(binX); + int iY = floor(binY); + double weightX = 1.-binX+iX; + double weightY = 1.-binY+iY; + + //linear f interpolation //TODO implement logscale f interpolation + double f_11 = fData->getColumn(iX)[iY]*weightX*weightY; + double f_21 = (weightX<1.)? (fData->getColumn(iX+1)[iY]*(1.-weightX)*weightY) : 0.; + double f_12 = (weightY<1.)? (fData->getColumn(iX)[iY+1]*weightX*(1.-weightY)) : 0.; + double f_22 = (weightX<1.&&weightY<1.)?(fData->getColumn(iX+1)[binY+1]*(1.-weightX)*(1.-weightY)) : 0.; + double result = f_11 + f_21 + f_12 + f_22; + return result; + } + + virtual X MinArg(int aArgNo) const {return aArgNo==1?xMin:yMin;}; + virtual X MaxArg(int aArgNo) const {return aArgNo==1?xMax:yMax;} + +private: + SafePtr<TableReaderX<X> > fData; + X xMin; + X yMin; + X xMax; + X yMax; + X xStep; + X yStep; + int nCols; + int nRows; + bool logScaleX; + bool logScaleY; +}; + +typedef MatrixFunctionX<double> MatrixFunction; + +} /* namespace Utils */ + +#endif /* TABLEFUNCTION_H_ */ diff --git a/src/lib/TableReader.cpp b/src/lib/TableReader.cpp new file mode 100644 index 0000000..147cd51 --- /dev/null +++ b/src/lib/TableReader.cpp @@ -0,0 +1,270 @@ +/* + * TableReader.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#include "TableReader.h" +#include <iterator> +#include <fstream> +#include <string.h> + +namespace Utils { + + template<typename X> TableReaderX<X>::TableReaderX(std::string aFile, int nColumns) { + std::ifstream file(aFile.c_str()); + if (file.eof()) + Exception::Throw("Failed to open file " + aFile); + try{ + init(file, nColumns); + } + catch(Exception* ex){ + std::string reason = ex->Message(); + delete ex; + Exception::Throw("Failed to parse " + aFile + " : " + reason); + } + } + + template<typename X> TableReaderX<X>::TableReaderX(std::istream& aInput, int nColumns) + { + init(aInput, nColumns); + } + + template<typename X> void TableReaderX<X>::init(std::istream& aInput, int nColumns) + { + std::string line; + bool firstRow=true; + while (std::getline(aInput, line)) + { + std::istringstream iss(line); + X val; + size_t col=0; + for(; iss >> val; col++){ + if(firstRow) + m_columns.push_back(new std::vector<X>()); + else if(col>=m_columns.size()) + Exception::Throw("variable number of records in a row"); + m_columns[col]->push_back(val); + } + if(col!=m_columns.size()) + Exception::Throw("variable number of records in a row"); + if(firstRow && nColumns!=Auto && col!=nColumns){ + Exception::Throw("unexpected number of records: " + ToString(col) + " in a row (" + ToString(nColumns) + " expected)"); + } + firstRow=false; + } + +// for(int i=0; i<nColumns; i++) +// m_columns.push_back(new std::vector<X>()); +// +// std::istream_iterator<X> eos; +// std::istream_iterator<X> iit (aInput); +// +// int recNo=0; +// for(; iit!=eos; recNo++, iit++) +// m_columns[recNo%nColumns]->push_back(*iit); +// +// if(recNo%nColumns) +// Exception::Throw("unexpected number of rows"); + } + + template<typename X> TableReaderX<X>::~TableReaderX() + { + for(int i=m_columns.size()-1; i>=0; i--) + delete m_columns[i]; + } + + template class TableReaderX<double>; + template class TableReaderX<long double>; + + CTableWithHeaderReader::CTableWithHeaderReader(const char* theFileName, char theCommentSimbol): + m_commentSymbol(theCommentSimbol), + m_curHeaderLine(0), + m_curDataLine(0) + { + m_file = fopen(theFileName,"rt"); + } + + CTableWithHeaderReader::~CTableWithHeaderReader() + { + + } + + bool CTableWithHeaderReader::readLine() + { + for(int i=0;i<TAB_READER_BUF_LENGTH;i++) + { + int ch = fgetc(m_file); + if(ch == EOF) + { + if(m_buffer[0] == m_commentSymbol) + i = 0; + m_buffer[i] = '\0'; + return i; + } + if(ch == '\n') + { + if((i==0)||(m_buffer[0] == m_commentSymbol)) + { + i = -1; + continue;//skipping empty strings and comments + } + else + { + m_buffer[i] = '\0'; + return true; + } + } + else + m_buffer[i] = ch; + } + Exception::Throw("CDataReader : buffer overflow"); + return false; + } + + bool CTableWithHeaderReader::read() + { + if(!m_file) + return false; + // bool result = false; + while(readLine()) + { + m_curHeaderLine++; + ECompleteStatus status = readHeaderLine(m_buffer); + switch(status) + { + case notCompletedE: continue; + case failedE: return false; + case completedE:; + } + break; + } + while(readLine()) + { + m_curDataLine++; + if(!readDataLine(m_buffer)) + return false; + } + if(!testData()) + return false; + processData(); + fclose(m_file); + m_file = NULL; + return true; + } + +/// reads number from standard file header line "data length <number>" +/// here number must indicate number of data lines below + bool CTableWithHeaderReader::readDataLength(int& length, const char* theString) + { + const char format[] = " data length %d"; + return (sscanf(theString,format,&length)==1); + } + + +/// parameter vector must not be created previously (using CVector::Create() method) +/// default delimiter string " /t/r/n" + bool CTableWithHeaderReader::readVector(std::vector<double>& v, const char* theString, const char* delimiterStr) + { + bool result = false; + char* stringCopy = 0; + try{ + const char defaultDelimStr[] = " \t\n\r"; + if (delimiterStr==NULL) + { + delimiterStr = defaultDelimStr; + } + + int length = strlen(theString); + stringCopy = new char[length + 1]; + strcpy(stringCopy, theString); + + int i=0; + char* curStr = strtok(stringCopy,delimiterStr); + + // calculating number of entries + while(curStr) + { + double val=0; + if (sscanf(curStr,"%lg",&val)!=1) + Exception::Throw("number format error"); + v.push_back(val); + curStr = strtok(NULL, delimiterStr); + i++; + } + if(i==0) + { + Exception::Throw("no tokens"); + } + + result = true; + } + catch(const char* aError) + { + //error saved and can be retrieved using LastError() method + } + delete[] stringCopy; + return result; + } + + double CTableWithHeaderReader::yValue(double xValue, double *xArray, double *yArray, int xArraySize, double leftValue, double rightValue) +//find the nearest to xValue x-array element (increasing x-array supposed) +//like CVector::findX function, and than using 1-st order approximation to find y value + { + int left = 0; + int right = xArraySize-1; + + if(xValue>xArray[right]) + return rightValue; + if(xValue<xArray[left]) + return leftValue; + + for(int i=(left+right)/2;right-left>1;i=(left+right)/2) + { + if(xValue>xArray[i]) + left=i; + else + right=i; + }//finding nearest point + + ASSERT((right - left) == 1); + + double result = yArray[left]+(xValue-xArray[left])*(yArray[right]-yArray[left])/(xArray[right]-xArray[left]); + return result; + } + + void CTableWithHeaderReader::inverseArray(double *pArray, unsigned int size) + { + unsigned int maxIndex = size/2; + unsigned int i1,i2; + for(i1=0,i2=size-1;i1<maxIndex;i1++,i2--) + { + double mem = pArray[i1]; + pArray[i1] = pArray[i2]; + pArray[i2] = mem; + } + } + +} /* namespace Utils */ diff --git a/src/lib/TableReader.h b/src/lib/TableReader.h new file mode 100644 index 0000000..3735347 --- /dev/null +++ b/src/lib/TableReader.h @@ -0,0 +1,105 @@ +/* + * TableReader.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef TABLEREADER_H_ +#define TABLEREADER_H_ + +#include <istream> +#include <vector> +#include "Utils.h" + +namespace Utils { + +template<typename X = double > +class TableReaderX : public SmartReferencedObj { +public: + enum{ + Auto=-1 + }; + //use TableReader::Auto for second argument if number of columns is not known in advance + TableReaderX(std::string aFile, int nColumns /* =Auto */); + TableReaderX(std::istream& aInput, int nColumns /* =Auto */); + virtual ~TableReaderX(); + inline std::vector<X>& getColumn(int i){return *(m_columns[i]);}; + inline const std::vector<X>& getColumn(int i) const {return *(m_columns[i]);}; + inline size_t numberOfColumns() const { return m_columns.size(); }; +protected: + void init(std::istream& aInput, int nColumns); + std::vector<std::vector<X>* > m_columns; +}; + +typedef TableReaderX<double> TableReader; + +#ifndef TAB_READER_BUF_LENGTH +#define TAB_READER_BUF_LENGTH 65536 +#endif + + class CTableWithHeaderReader + { + public: + static void inverseArray(double* pArray, unsigned int size); + static double yValue(double xValue, double* xArray, double* yArray,int xArraySize, double leftValue=0, double rightValue=0); + enum ECompleteStatus + { + notCompletedE = -1, + failedE = 0, + completedE = 1 + }; + CTableWithHeaderReader(const char* theFileName, char theCommentSimbol = '#'); + virtual bool read(); + virtual ~CTableWithHeaderReader(); + + /// reads number from standard file header line "data length <number>" + /// here number must indicate number of data lines below + /// usually called from readHeaderLine + /// parameter theString is header string passed to readHeaderLine + static bool readDataLength(int& length, const char* theString); + + /// the data read will be appended to vector + /// default delimiter string " /t/r/n" + static bool readVector(std::vector<double>& aVector, const char* aString, const char* aDelimiterStr=NULL); + + protected: + virtual ECompleteStatus readHeaderLine(const char* theString){return completedE;}; + + //readDataLine returns false in case of error + virtual bool readDataLine(const char* theString) = 0; + virtual bool testData(){return true;}; + virtual void processData(){}; + + char m_commentSymbol; + int m_curHeaderLine;//starting from 1 + int m_curDataLine;//starting from 1 + private: + bool readLine();//returns false if EOF was reached + + char m_buffer[TAB_READER_BUF_LENGTH]; + FILE* m_file; + }; + +} /* namespace Utils */ +#endif /* TABLEREADER_H_ */ diff --git a/src/lib/Test.cpp b/src/lib/Test.cpp new file mode 100644 index 0000000..f279344 --- /dev/null +++ b/src/lib/Test.cpp @@ -0,0 +1,1414 @@ +/* + * Test.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "Test.h" +#include <cstdlib> +#include "ParticleStack.h" +#include "Utils.h" +#include "Cosmology.h" +#include "PropagationEngine.h" +#include <iostream> +#include "Output.h" +#include "TableBackgrounds.h" +#include "ICS.h" +#include "GammaPP.h" +#include "ProtonPP.h" +#include "PPP.h" +#include "GZK.h" +#include "Deflection3D.h" +#include "NeutronDecay.h" +#include "SteckerEBL.h" +#include "PhotoDisintegration.h" +#include "Stecker16Background.h" + +#ifdef ELMAG_TEST +#include "ElmagTest.h" +#endif + +using namespace std; +using namespace mcray; +using namespace Utils; +using namespace Backgrounds; +using namespace Interactions; + +namespace Test { + +void BuildInitialState(ParticleStack& particles, double aZmax) +{ + Particle gamma(Photon, aZmax); + gamma.Energy = 1e7/units.Eunit;//MeV + + particles.AddPrimary(gamma); +} + +void TestList::EngineTest(double aAlphaThinning) +{ + class SimpleSplitting : public RandomInteraction + { + public: + SimpleSplitting(double aRate):fRate(aRate){} + RandomInteraction* Clone() const { return new SimpleSplitting(fRate); } + virtual ~SimpleSplitting(){}; + double Rate(const Particle& aParticle) const {return fRate;} + bool GetSecondaries(Particle& aParticle, std::vector<Particle>& aSecondaries, Randomizer& aRandomizer) const + { + Particle product = aParticle; + product.Energy = 0.5 * product.Energy; + aSecondaries.push_back(product); + aSecondaries.push_back(product); + return true; + } + private: + double fRate; + }; + double Zmax = 0.01; + double Emin = 1.; + double Emax = 1000.; + int Ninteractions = 100; + + ParticleStack particles; + Result result(Emin); + string outFile = "engine_alpha_" + ToString(aAlphaThinning); + result.AddOutput(new SpectrumOutput(outFile, Emin, pow(10, 0.05))); + + cosmology.Init(); + + PropagationEngine pe(particles, result, 2011); + EnergyBasedThinning thinning(aAlphaThinning); + pe.SetThinning(&thinning); + pe.AddInteraction(new SimpleSplitting(Ninteractions/cosmology.z2t(Zmax))); + Randomizer r; + + /// build initial state + double t1 = cosmology.z2t(Zmax); + double t2 = cosmology.z2t(0.); + int Nparticles = 10000000; + double mult = pow(10, 0.05); + int nBins = (int)(log(Emax/Emin)/log(mult) + 0.5); + int nParticlesPerBin = Nparticles/nBins; + double E=Emin*mult; + for(int i=0; i<nBins; i++, E*=mult) + { + for(int j=0; j<nParticlesPerBin; j++) + { + double t = t1+r.Rand()*(t2-t1); + Particle p(Photon, cosmology.t2z(t)); + p.Energy = E; + p.Weight = 1/E;///dN/dlogE + particles.AddPrimary(p); + } + } + pe.Run(); +} + +void TestList::FrameworkTest() +{ + double Zmax = 2; + double Emin = 1./units.Eunit; + //double alphaThinning = 0;//alpha = 1 conserves number of particles on the average; alpha = 0 disables thinning + ParticleStack particles; + Result result(Emin); + result.AddOutput(new SpectrumOutput("out", Emin, pow(10, 0.05))); + + cosmology.Init(); + + CompoundBackground backgr; + double cmbTemp = 2.73/Units::phTemperature_mult/units.Eunit; + + backgr.AddComponent(new PlankBackground(cmbTemp, 1e-5*cmbTemp, 1e3*cmbTemp)); + //backgr.AddComponent(new Backgrounds::Kneiske1001EBL()); + //double stepZ = 0.1; + //double logStepK = pow(10,0.05); + //double epsRel = 1e-3; + + //BackgroundIntegral backgrI(backgr, stepZ, logStepK, Zmax, epsRel); + + PropagationEngine pe(particles, result, 2011); + //EnergyBasedThinning thinning(alphaThinning); + //pe.SetThinning(&thinning); + pe.AddInteraction(new RedShift()); + //pe.AddInteraction(new Interactions::ICS(backgrI,Emin)); + //pe.AddInteraction(new Interactions::IcsCEL(backgrI,Emin)); + //pe.AddInteraction(new Interactions::GammaPP(backgrI)); + + /// build initial state + BuildInitialState(particles, Zmax); + + pe.Run(); +} + +int TestList::Main(int argc, char** argv) +{ + if(argc==0) + { + FrameworkTest(); + return 0; + } + const char* command = argv[0]; + std::map<std::string,TestProc>::iterator tit = fTests.find(command); + if(tit!=fTests.end()){ + TestProc proc = tit->second; + return proc(); + } + + + + if(strcmp(command,"elmag")==0) + { +#ifdef ELMAG_TEST + ElmagTests::Main(argc-1, argv+1); +#else + Exception::Throw("Elmag tests are not linked in this version"); +#endif + } + else if(strcmp(command,"FERMI")==0) + FermiTests::Main(argc-1, argv+1); + else if(strcmp(command,"cosmology")==0) + Cosmology::UnitTest(); + else if(strcmp(command,"background")==0) + BackgroundTest(); + else if(strcmp(command,"backgroundI")==0) + ContinuousBackgroundIntegral::UnitTest(); + else if(strcmp(command,"GZK")==0) + //GZK::UnitTest(); + GZK::UnitTestEconserv(1e20, 0.1); + else if(strcmp(command,"ICS")==0) + { + //ICS::UnitTest(false, true, true,1./units.Eunit, 1e6/units.Eunit, 1e-6); + //ICS::UnitTest(false, true, false,1./units.Eunit, 1e6/units.Eunit, 1e-6); + //ICS::UnitTest(false, true, true,100./units.Eunit, 1e6/units.Eunit, 1e-6); + //ICS::UnitTest(false, true, false,100./units.Eunit, 1e6/units.Eunit, 1e-6); + + ICS::UnitTest(true, false, false,1./units.Eunit, 1e14/units.Eunit, 1e-6);//print rates only + ICS::UnitTest(true, false, true,1./units.Eunit, 1e14/units.Eunit, 1e-6);//print rates only + + //ICS::UnitTest(true, false, false); + //ICS::UnitTest(true, false, true); + + //ICS::UnitTestMono(true, 1, true); + //ICS::UnitTestMono(false, 1, true); + //ICS::UnitTestMono(false, 10, true); + //ICS::UnitTestMono(false, 100, true); + //ICS::UnitTestMono(false, 1000, true); + //ICS::UnitTest(); + } + else if(strcmp(command,"ICSsample")==0) + { + ICS::UnitTestSampling(1./units.Eunit, 1e8/units.Eunit, 1e-4); + } + else if(strcmp(command,"ICSdistr")==0) + { + ICS::UnitTestDistribution(1./units.Eunit, 1e8/units.Eunit, 1e-4); + } + else if(strcmp(command,"ICScel")==0) + IcsCEL::UnitTest(); + else if(strcmp(command,"PP")==0) + { + //GammaPP::UnitTest(); + //GammaPP::UnitTest(true, true, true); + //GammaPP::UnitTest(true, true, false); + GammaPP::UnitTest(true, false, false, false);//rates only + GammaPP::UnitTest(true, false, false, true);//rates only + } + else if(strcmp(command,"PPsample")==0) + { + GammaPP::UnitTestSampling(1e8/units.Eunit, 1e-4); + } + else if(strcmp(command,"PPP")==0) + { + PPP::UnitTest(); + //ProtonPP::UnitTest(); + } + else if(strcmp(command,"split")==0) + { + SplittedBackgroundTest(true); + SplittedBackgroundTest(false); + } + else if(strcmp(command,"thinning")==0) + { + ThinningTest(1); + ThinningTest(0.5); + ThinningTest(0); + } + else if(strcmp(command,"Kachelries")==0) + {// KachelriesTest(double aEmax_eV, double aZmax, double aAlphaThinning, bool aSplitted, int aN, bool aNoPP, bool aNoICS, ParticleType aPrimary); + if(argc!=12 && argc!=13)//E${E}_z${z}_N${N}_prim${PrimaryPart}_thin${alpha} + { + Exception::Throw("usage: --test Kachelries Emin/eV E/eV z N primary alpha_thinning NoPP NoICSdiscr NoICScel NoRedshift split [output/dir]\nprimary: 0 - photon; -1 - e-; 1 - e+"); + } + double E,z,alpha,Emin; + int N,NoPP,NoICSdiscr,NoICScel,NoRedshift,particle,split; + int pos = 1; + if(sscanf(argv[pos++],"%lg", &Emin)!=1) + Exception::Throw("Failed to read Emin parameter"); + if(sscanf(argv[pos++],"%lg", &E)!=1) + Exception::Throw("Failed to read E parameter"); + if(sscanf(argv[pos++],"%lg", &z)!=1) + Exception::Throw("Failed to read z parameter"); + if(sscanf(argv[pos++],"%d", &N)!=1) + Exception::Throw("Failed to read N parameter"); + if(sscanf(argv[pos++],"%d", &particle)!=1) + Exception::Throw("Failed to read primary parameter"); + ParticleType pt = ParticleTypeEOF; + switch(particle) + { + case 0: pt = Photon; break; + case 1: pt = Positron; break; + case -1: pt = Electron; break; + default: + Exception::Throw("Invalid 'primary' parameter"); + break; + } + if(sscanf(argv[pos++],"%lg", &alpha)!=1) + Exception::Throw("Failed to read alpha_thinning parameter"); + if(sscanf(argv[pos++],"%d", &NoPP)!=1) + Exception::Throw("Failed to read NoPP parameter"); + if(sscanf(argv[pos++],"%d", &NoICSdiscr)!=1) + Exception::Throw("Failed to read NoICSdiscr parameter"); + if(sscanf(argv[pos++],"%d", &NoICScel)!=1) + Exception::Throw("Failed to read NoICScel parameter"); + if(sscanf(argv[pos++],"%d", &NoRedshift)!=1) + Exception::Throw("Failed to read NoRedshift parameter"); + if(sscanf(argv[pos++],"%d", &split)!=1) + Exception::Throw("Failed to read split parameter"); + const char* outputDir = argc==13 ? argv[pos++] : 0; + + KachelriesTest(Emin, E, z, alpha, split!=0, N, NoPP!=0, NoICSdiscr!=0, NoICScel!=0, NoRedshift!=0, pt, outputDir); + } + else if(strcmp(command,"mono")==0) + { + //MonochromaticAndGausianBackgroundTest(true,true,false,false); + MonochromaticAndGausianBackgroundTest(true,false,true,false); + //MonochromaticAndGausianBackgroundTest(true,false,false,true); + //MonochromaticAndGausianBackgroundTest(false,true,false,false); + MonochromaticAndGausianBackgroundTest(false,false,true,false); + //MonochromaticAndGausianBackgroundTest(false,false,false,true); + } + else if(strcmp(command,"OpenMP")==0) + { + MultithreadTest(false); + MultithreadTest(true); + } + else if(strcmp(command,"bi")==0) + { + BichromaticTest(true); + } + else if(strcmp(command,"sampling")==0) + { + SamplingTest(); + } + else if(strcmp(command,"engine")==0) + { + EngineTest(0.5); + } + return 0; +} + +void TestList::SamplingTest() +{ + ParamlessFunction aDistrib(sin); + double aRand = 0.5; + double aOutputX; + double aOutputIntegral; + double xMin = 0; + double xMax = M_PI; + double aRelError = 1e-4; + //SampleDistribution(const Function& aDistrib, double aRand, double& aOutputX, double& aOutputIntegral, double aInitialStep, double xMin, double xMax, double aRelError); + MathUtils::SampleDistribution(aDistrib, aRand, aOutputX, aOutputIntegral, xMin, xMax, aRelError); + + aOutputX/=M_PI; + aOutputX*=M_PI; + + + class SamplingTestFunc : public Function + { + GaussianBackground fTestFunc; + public: + SamplingTestFunc(): + fTestFunc(1e10, 1e8, 99, 1) + { + + } + double f(double aX) const + { + return fTestFunc.n(aX, 0.)/aX; + } + virtual double Xmin() const {return fTestFunc.MinE();} + virtual double Xmax() const {return fTestFunc.MaxE();} + }; + std::ofstream samplingOut; + samplingOut.open("samplingOld",std::ios::out); + std::ofstream samplingOutNew; + samplingOutNew.open("sampling",std::ios::out); + Randomizer r(0); + SamplingTestFunc f; + std::ofstream funcOut; + funcOut.open("comulative.f",std::ios::out); + double step = pow(f.Xmax()/f.Xmin(),0.001); + double sum = 0; + funcOut << f.Xmin() << "\t0\n"; + MathUtils math; + for(double x = f.Xmin(); x<f.Xmax(); x*=step) + { + sum+=math.Integration_qag(f,x,x*step,1e-300,1e-5,1000); + funcOut << x << "\t" << sum << "\n"; + } + funcOut.close(); + + int nSteps = 100; + xMin=10; + xMax=1e20; + aRelError = 1e-3; + double x; + double I; + int nTrials = 100000; + for(int i=0; i<nTrials; i++) + { + double rand = r.Rand(); + math.SampleLogscaleDistribution<double>(f, rand, x, I, nSteps, xMin, xMax, aRelError); + samplingOut << x << "\n"; + MathUtils::SampleLogDistribution(f, rand, x, I, xMin, xMax, aRelError); + samplingOutNew << x << "\n"; + + } + samplingOut.close(); + samplingOutNew.close(); + std::cout << "prepare randomly generated comulative distribution file with command:\n" + <<"sort -g sampling | awk ' {SUM=SUM+1; printf(\"%g\\t%d\\n\",$1,SUM)}' > comulative.r\n" + <<"and compare it to comulative.f using gnuplot command\n\t" + <<"pl 'comulative.r' u 1:(" << 1.0/nTrials << "*$2) w l, 'comulative.f'" << endl; + +} + +void TestList::BackgroundTest() +{ + double zMax = 7; + //cosmology.init(0.73, 71, 10); + + CompoundBackground backgr; + double cmbTemp = 2.73/Units::phTemperature_mult/units.Eunit; + IBackground* plank = new PlankBackground(cmbTemp, 1e-3*cmbTemp, 1e3*cmbTemp, 0, zMax); + IBackground* kneiske = new Kneiske0309EBL(); + double conc = 413*units.Vunit;//413 cm^{-3} + double centralE = 6.3e-10/units.Eunit; + GaussianBackground backgrGauss(centralE, 0.1*centralE, 1., conc); + backgr.AddComponent(plank); + backgr.AddComponent(kneiske); + ofstream plankout; + plankout.open("backgr_plank",ios::out); + BackgroundTest(*plank, plankout); + ofstream kneiskeout; + kneiskeout.open("backgr_kneiskeBF",ios::out); + BackgroundTest(*kneiske, kneiskeout); + ofstream gaussout; + gaussout.open("backgr_gauss",ios::out); + BackgroundTest(backgrGauss, gaussout); + ofstream sumout; + sumout.open("backgr_sum",ios::out); + BackgroundTest(backgr, sumout); +} + +void TestList::BackgroundTest(const IBackground& aBackground, std::ostream& aOut) +{ + int kIntervals = 100; + int zIntervals = aBackground.MaxZ()-aBackground.MinZ(); + if(zIntervals<1) + zIntervals = 2; + double zStep = (aBackground.MaxZ()-aBackground.MinZ())/zIntervals;//usually zStep=1; + double kStep = pow(aBackground.MaxE()/aBackground.MinE(), 1./kIntervals); + double z = aBackground.MinZ(); + aOut << "# " << aBackground.Name() << " background\n#col1: E/eV\t\tcol2: k dn(k)/dk /cm^{-3}\n"; + for(int iZ=0; iZ<=zIntervals; iZ++, z+=zStep) + { + aOut << "#z=" << z << "\n"; + double k = aBackground.MinE(); + for(int iK=0; iK<=kIntervals; iK++, k*=kStep) + aOut << k*units.Eunit*1e6 << "\t" << aBackground.n(k,z)/units.Vunit << "\n"; + aOut << "\n\n"; + } +} + + +void TestList::SplittedBackgroundTest(bool aSplitted) { + if(!cosmology.IsInitialized()) + cosmology.Init(); + double Zmax = 0.15; + double Emin = 1./units.Eunit; + double alphaThinning = 1;//alpha = 1 conserves number of particles on the average; alpha = 0 disables thinning + ParticleStack particles; + Result result(Emin); + result.AddOutput(new SpectrumOutput(aSplitted?"out_split" : "out_no_split", Emin, pow(10, 0.05))); + + CompoundBackground backgr; + + //double cmbTemp = 2.73/Units::phTemperature_mult/units.Eunit; + IBackground* b1 = new GaussianBackground(1e-6/units.Eunit, 1e-7/units.Eunit, 1., 1*units.Vunit); + IBackground* b2 = new GaussianBackground(6.3e-10/units.Eunit, 1e-11/units.Eunit, 1., 413*units.Vunit); + //IBackground* b1 = new PlankBackground(cmbTemp, 1e-3*cmbTemp, 1e3*cmbTemp); + //IBackground* b2 = new Kneiske1001EBL(); + //double n = BackgroundUtils::CalcIntegralDensity(*b1)/units.Vunit; + + backgr.AddComponent(b1); + backgr.AddComponent(b2); + + + double stepZ = Zmax<0.2 ? Zmax/2 : 0.1; + double logStepK = pow(10,0.05); + double epsRel = 1e-3; + + PBackgroundIntegral backgrI = new ContinuousBackgroundIntegral(backgr, stepZ, logStepK, Zmax, epsRel); + PBackgroundIntegral backgrI1 = new ContinuousBackgroundIntegral(*b1, stepZ, logStepK, Zmax, epsRel); + PBackgroundIntegral backgrI2 = new ContinuousBackgroundIntegral(*b1, stepZ, logStepK, Zmax, epsRel); + + + PropagationEngine pe(particles, result, 2011); + EnergyBasedThinning thinning(alphaThinning); + pe.SetThinning(&thinning); + pe.AddInteraction(new RedShift()); + if(aSplitted) + { + pe.AddInteraction(new Interactions::ICS(backgrI1,Emin)); + pe.AddInteraction(new Interactions::IcsCEL(backgrI1,Emin)); + pe.AddInteraction(new Interactions::GammaPP(backgrI1)); + pe.AddInteraction(new Interactions::ICS(backgrI2,Emin)); + pe.AddInteraction(new Interactions::IcsCEL(backgrI2,Emin)); + pe.AddInteraction(new Interactions::GammaPP(backgrI2)); + } + else + { + pe.AddInteraction(new Interactions::ICS(backgrI,Emin)); + pe.AddInteraction(new Interactions::IcsCEL(backgrI,Emin)); + pe.AddInteraction(new Interactions::GammaPP(backgrI)); + } + + int nParticles = 10000; + for(int i=0; i<nParticles; i++) + { + Particle gamma(Photon, Zmax); + gamma.Energy = 1e8/units.Eunit;//MeV + particles.AddPrimary(gamma); + } + + pe.Run(); +} + +void TestList::ThinningTest(double aAlpha) +{ + if(!cosmology.IsInitialized()) + cosmology.Init(); + double logStepK = pow(10,0.05); + double Zmax = 0.01; + double Emin = 1./units.Eunit; + double alphaThinning = aAlpha;//alpha = 1 conserves number of particles on the average; alpha = 0 disables thinning + ParticleStack particles; + Result result(Emin); + result.AddOutput(new SpectrumOutput("out_alpha" + ToString(aAlpha), Emin, logStepK)); + + CompoundBackground backgr; + + double cmbTemp = 2.73/Units::phTemperature_mult/units.Eunit; + //IBackground* b1 = new GaussianBackground(1e-6/units.Eunit, 1e-7/units.Eunit, 1., 1*units.Vunit); + //IBackground* b2 = new GaussianBackground(6.3e-10/units.Eunit, 1e-11/units.Eunit, 1., 413*units.Vunit); + IBackground* b1 = new PlankBackground(cmbTemp, 1e-3*cmbTemp, 1e3*cmbTemp); + //IBackground* b2 = new Kneiske1001EBL(); + //double n = BackgroundUtils::CalcIntegralDensity(*b1)/units.Vunit; + + backgr.AddComponent(b1); + //backgr.AddComponent(b2); + + + double stepZ = Zmax<0.2 ? Zmax/2 : 0.1; + double epsRel = 1e-3; + + PBackgroundIntegral backgrI = new ContinuousBackgroundIntegral(backgr, stepZ, logStepK, Zmax, epsRel); + + + PropagationEngine pe(particles, result, 2011); + EnergyBasedThinning thinning(alphaThinning); + pe.SetThinning(&thinning); + pe.AddInteraction(new RedShift()); + + { + pe.AddInteraction(new Interactions::ICS(backgrI,Emin)); + pe.AddInteraction(new Interactions::IcsCEL(backgrI,Emin)); + pe.AddInteraction(new Interactions::GammaPP(backgrI)); + } + + int nParticles = 1000; + for(int i=0; i<nParticles; i++) + { + Particle gamma(Photon, Zmax); + gamma.Energy = 1e8/units.Eunit;//MeV + particles.AddPrimary(gamma); + } + + pe.Run(); +} + +void TestList::MonochromaticAndGausianBackgroundTest(bool aMonochromatic, bool aICS, bool aICScel, bool aPP) +{ + if(!cosmology.IsInitialized()) + cosmology.Init(); + double logStepK = pow(10,0.05); + double Zmax = 0.01; + double Emin = 1./units.Eunit; + double alphaThinning = 0.5;//alpha = 1 conserves number of particles on the average; alpha = 0 disables thinning + ParticleStack particles; + Result result(Emin); + std::string out = aMonochromatic ? "out_mono" : "out_gauss"; + if(aICS) + out = out + "_ICS"; + if(aICScel) + out = out + "_ICScel"; + if(aPP) + out = out + "_PP"; + + result.AddOutput(new SpectrumOutput(out , Emin, logStepK)); + + double conc = 413*units.Vunit;//413 cm^{-3} + double centralE = 6.3e-10/units.Eunit; + ConstFunction k(centralE); + ConstFunction c(conc); + + GaussianBackground backgr(centralE, 0.1*centralE, 1., conc); + + double stepZ = Zmax<0.2 ? Zmax/2 : 0.1; + double epsRel = 1e-3; + + PropagationEngine pe(particles, result, 2011); + EnergyBasedThinning thinning(alphaThinning); + pe.SetThinning(&thinning); + pe.AddInteraction(new RedShift()); + PBackgroundIntegral backgrM = new MonochromaticBackgroundIntegral(c, k, logStepK, epsRel); + PBackgroundIntegral backgrC = new ContinuousBackgroundIntegral(backgr, stepZ, logStepK, Zmax, epsRel); + BackgroundIntegral* backgrI = aMonochromatic ? backgrM : backgrC; + + if(aICS) + pe.AddInteraction(new Interactions::ICS(backgrI,Emin)); + if(aICScel) + pe.AddInteraction(new Interactions::IcsCEL(backgrI,Emin)); + if(aPP) + pe.AddInteraction(new Interactions::GammaPP(backgrI)); + + int nParticles = 3000; + for(int i=0; i<nParticles; i++) + { + Particle startParticle(aPP?Photon:Electron, Zmax); + startParticle.Energy = 1e9/units.Eunit;//MeV + particles.AddPrimary(startParticle); + } + + //pe.RunMultithread(); + pe.Run(); +} + +// +void TestList::BichromaticTest(bool aGauss) +{ + if(!cosmology.IsInitialized()) + cosmology.Init(); + double logStepK = pow(10,0.05); + double Zmax = 1.; + double Emin = 1./units.Eunit; + double alphaThinning = 0.5;//alpha = 1 conserves number of particles on the average; alpha = 0 disables thinning + ParticleStack particles; + Result result(Emin); + std::string out = "bichromatic_"; + out += aGauss ? "gauss" : "mono"; + + result.AddOutput(new SpectrumOutput(out , Emin, logStepK)); + + double centralE1 = 6.3e-10/units.Eunit;//6.3e-4eV + double n1 = 413*units.Vunit;//413cm^-3 + ConstFunction k1(centralE1); + ConstFunction c1(n1); + GaussianBackground g1(centralE1, 0.1*centralE1, 1., n1); + + double centralE2 = 1e-6/units.Eunit;//1 eV + double n2 = 1.*units.Vunit;//1 cm^-3 + ConstFunction k2(centralE2); + ConstFunction c2(n2); + GaussianBackground g2(centralE2, 0.1*centralE2, 1., n2); + + double epsRel = 1e-3; + + PropagationEngine pe(particles, result, 2011); + EnergyBasedThinning thinning(alphaThinning); + pe.SetThinning(&thinning); + //pe.AddInteraction(new RedShift()); + PBackgroundIntegral I1,I2; + if(aGauss) + { + I1 = new ContinuousBackgroundIntegral(g1, 0.2, logStepK, Zmax, epsRel); + I2 = new ContinuousBackgroundIntegral(g2, 0.2, logStepK, Zmax, epsRel); + } + else + { + I1 = new MonochromaticBackgroundIntegral(c1, k1, logStepK, epsRel); + I2 = new MonochromaticBackgroundIntegral(c2, k2, logStepK, epsRel); + } + pe.AddInteraction(new Interactions::ICS(I1,Emin)); + pe.AddInteraction(new Interactions::IcsCEL(I1,Emin)); + pe.AddInteraction(new Interactions::GammaPP(I1)); + pe.AddInteraction(new Interactions::ICS(I2,Emin)); + pe.AddInteraction(new Interactions::IcsCEL(I2,Emin)); + pe.AddInteraction(new Interactions::GammaPP(I2)); + + int nParticles = 1000; + for(int i=0; i<nParticles; i++) + { + Particle startParticle(Photon, Zmax); + startParticle.Energy = 1e9/units.Eunit;//MeV + particles.AddPrimary(startParticle); + } + + pe.RunMultithread(); + //pe.Run(); +} + +void TestList::MultithreadTest(bool aMultithread) +{ + if(!cosmology.IsInitialized()) + cosmology.Init(); + double logStepK = pow(10,0.05); + double Zmax = 0.01; + double Emin = 1./units.Eunit; + double alphaThinning = 0;//0.5;//alpha = 1 conserves number of particles on the average; alpha = 0 disables thinning + ParticleStack particles; + Result result(Emin); + result.AddOutput(new SpectrumOutput(aMultithread ? "out_multithread" : "out_singlethread" , Emin, logStepK)); + + double conc = 413*units.Vunit;//413 cm^{-3} + double centralE = 6.3e-10/units.Eunit; + ConstFunction k(centralE); + ConstFunction c(conc); + + GaussianBackground backgr(centralE, 0.1*centralE, 1., conc); + + double stepZ = Zmax<0.2 ? Zmax/2 : 0.1; + double epsRel = 1e-3; + + PropagationEngine pe(particles, result, 2011); + EnergyBasedThinning thinning(alphaThinning); + pe.SetThinning(&thinning); + pe.AddInteraction(new RedShift()); + PBackgroundIntegral backgrM = new MonochromaticBackgroundIntegral(c, k, logStepK, epsRel); + PBackgroundIntegral backgrC = new ContinuousBackgroundIntegral(backgr, stepZ, logStepK, Zmax, epsRel); + + pe.AddInteraction(new Interactions::ICS(backgrM,Emin)); + pe.AddInteraction(new Interactions::IcsCEL(backgrM,Emin)); + pe.AddInteraction(new Interactions::GammaPP(backgrM)); + pe.AddInteraction(new Interactions::ICS(backgrC,Emin)); + pe.AddInteraction(new Interactions::IcsCEL(backgrC,Emin)); + pe.AddInteraction(new Interactions::GammaPP(backgrC)); + + int nParticles = 30; + for(int i=0; i<nParticles; i++) + { + Particle gamma(Photon, Zmax); + gamma.Energy = 1e9/units.Eunit;//MeV + particles.AddPrimary(gamma); + } + + if(aMultithread) + pe.RunMultithread(); + else + pe.Run(); +} + +//this test is used to compare calculations of this code with Elmag http://uk.arxiv.org/pdf/1106.5508v2 +void TestList::KachelriesTest(double aEmin_eV, double aEmax_eV, double aZmax, + double aAlpha, bool aSplitted, int nParticles, + bool noPP, bool noICS, bool noICScel, bool aNoRedshift, + ParticleType primary, const char* aOutputDir) +{ + int kAc = 1; + double Emax = aEmax_eV;//eV + Emax *= (1e-6/units.Eunit); + if(!cosmology.IsInitialized()) + cosmology.Init();// Elmag 2.0 uses H=70 km/sec/Mpc (I checked this by measuring dt/dz at z=0 + // in subroutine cascade(icq,e00,weight0,z_in) + // H = z_in/((1.d0-t)*t_0)/3.2407792903e-20 + // Omega_vac = 0.7 as it was stated in http://uk.arxiv.org/pdf/1106.5508v2 page 6 (section 2.6. Cosmology) + double logStepK = pow(10,0.05/kAc); + double Emin = aEmin_eV*1e-6/units.Eunit; + double alphaThinning = aAlpha;//alpha = 1 conserves number of particles on the average; alpha = 0 disables thinning + ParticleStack particles; + Result result(Emin); + std::string outputDir; + if(aOutputDir) + outputDir = aOutputDir; + else + { + outputDir = Particle::Name(primary); + outputDir += ("_E" + ToString(aEmax_eV) + "_z" + ToString(aZmax) + "_N" + ToString(nParticles)); + if(noPP) + outputDir += "_NoPP"; + if(noICS) + outputDir += "_NoICSdiscr"; + if(noICScel) + outputDir += "_NoICScel"; + if(aNoRedshift) + outputDir += "_NoRedshift"; + if(aSplitted) + outputDir += "_splited"; + } + + result.AddOutput(new RawOutput(outputDir, true)); + result.AddOutput(new SpectrumOutput(outputDir + "/spec", Emin, pow(10,0.05))); + + CompoundBackground backgr; + + double cmbTemp = 2.73/Units::phTemperature_mult/units.Eunit; + //IBackground* b1 = new GaussianBackground(1e-6/units.Eunit, 1e-7/units.Eunit, 1., 1*units.Vunit); + //IBackground* b2 = new GaussianBackground(6.3e-10/units.Eunit, 1e-11/units.Eunit, 1., 413*units.Vunit); + IBackground* b1 = new PlankBackground(cmbTemp, 1e-3*cmbTemp, 1e3*cmbTemp); + IBackground* b2 = new ElmagKneiskeBestFit(false/*using wrong background intentionally to compare to Elmag*/); + //double n = BackgroundUtils::CalcIntegralDensity(*b1)/units.Vunit; + + backgr.AddComponent(b1); + backgr.AddComponent(b2); + + + double stepZ = aZmax<0.05 ? aZmax/2 : 0.025; + double epsRel = 1e-3; + + PBackgroundIntegral backgrI = new ContinuousBackgroundIntegral(backgr, stepZ, logStepK, aZmax, epsRel); + PBackgroundIntegral backgrI1 = new ContinuousBackgroundIntegral(*b1, stepZ, logStepK, aZmax, epsRel); + PBackgroundIntegral backgrI2 = new ContinuousBackgroundIntegral(*b2, stepZ, logStepK, aZmax, epsRel); + + PropagationEngine pe(particles, result, 2011); + EnergyBasedThinning thinning(alphaThinning); + pe.SetThinning(&thinning); + if(!aNoRedshift) + pe.AddInteraction(new RedShift()); + Particle electron(Electron, aZmax); + Particle photon(Photon, aZmax); + int nSteps = log(Emax/Emin)/log(logStepK)+1.; + double mult = pow(Emax/Emin, 1./nSteps); + int step; + if(aSplitted) + { + if(!noICS) + { + std::ofstream rateOut; + rateOut.open((outputDir + "/ICS").c_str(),std::ios::out); + PRandomInteraction i1,i2; + pe.AddInteraction((i1 = new Interactions::ICS(backgrI1,Emin))); + pe.AddInteraction((i2 = new Interactions::ICS(backgrI2,Emin))); + for(step=0, electron.Energy=Emin; step<=nSteps; electron.Energy*=mult, step++) + { + double r1 = i1->Rate(electron)*units.Mpc; + double r2 = i2->Rate(electron)*units.Mpc; + rateOut << electron.Energy * units.Eunit * 1e6 << "\t" << r1+r2 << "\t" << r1 << "\t" << r2 << "\n"; + } + rateOut.close(); + } + if(!noICScel) + { + std::ofstream rateOut; + rateOut.open((outputDir + "/ICScel").c_str(),std::ios::out); + PCELInteraction i1,i2; + pe.AddInteraction((i1 = new Interactions::IcsCEL(backgrI1,Emin))); + pe.AddInteraction((i2 = new Interactions::IcsCEL(backgrI2,Emin))); + for(step=0, electron.Energy=Emin; step<=nSteps; electron.Energy*=mult, step++) + { + double r1 = i1->Rate(electron)*units.Mpc; + double r2 = i2->Rate(electron)*units.Mpc; + rateOut << electron.Energy * units.Eunit * 1e6 << "\t" << r1+r2 << "\t" << r1 << "\t" << r2 << "\n"; + } + rateOut.close(); + } + if(!noPP) + { + std::ofstream rateOut; + rateOut.open((outputDir + "/PP").c_str(),std::ios::out); + PRandomInteraction i1,i2; + pe.AddInteraction((i1 = new Interactions::GammaPP(backgrI1))); + pe.AddInteraction((i2 = new Interactions::GammaPP(backgrI2))); + for(step=0, photon.Energy=Emin; step<=nSteps; photon.Energy*=mult, step++) + { + double r1 = i1->Rate(photon)*units.Mpc; + double r2 = i2->Rate(photon)*units.Mpc; + rateOut << photon.Energy * units.Eunit * 1e6 << "\t" << r1+r2 << "\t" << r1 << "\t" << r2 << "\n"; + } + rateOut.close(); + } + } + else + { + if(!noICS) + { + std::ofstream rateOut; + rateOut.open((outputDir + "/ICS").c_str(),std::ios::out); + PRandomInteraction i = new Interactions::ICS(backgrI,Emin); + pe.AddInteraction(i); + for(step=0, electron.Energy=Emin; step<=nSteps; electron.Energy*=mult, step++) + rateOut << electron.Energy * units.Eunit * 1e6 << "\t" << i->Rate(electron)*units.Mpc << "\n"; + rateOut.close(); + } + if(!noICScel) + { + std::ofstream rateOut; + rateOut.open((outputDir + "/ICScel").c_str(),std::ios::out); + PCELInteraction i = new Interactions::IcsCEL(backgrI,Emin); + pe.AddInteraction(i); + for(step=0, electron.Energy=Emin; step<=nSteps; electron.Energy*=mult, step++) + rateOut << electron.Energy * units.Eunit * 1e6 << "\t" << i->Rate(electron)*units.Mpc << "\n"; + rateOut.close(); + } + if(!noPP) + { + std::ofstream rateOut; + rateOut.open((outputDir + "/PP").c_str(),std::ios::out); + PRandomInteraction i = new Interactions::GammaPP(backgrI); + pe.AddInteraction(i); + for(step=0, photon.Energy=Emin; step<=nSteps; photon.Energy*=mult, step++) + rateOut << photon.Energy * units.Eunit * 1e6 << "\t" << i->Rate(photon)*units.Mpc << "\n"; + rateOut.close(); + } + } + + for(int i=0; i<nParticles; i++) + { + //if(i%100==0) + // std::cerr << i << " of " << nParticles << " ready" << std::endl; + Particle gamma(primary, aZmax); + gamma.Energy = Emax;//MeV + particles.AddPrimary(gamma); + //pe.RunMultithread(); + } +#ifdef _DEBUG + pe.Run(); +#else + pe.RunMultithread(); + //pe.Run(); +#endif +} + +void FermiTests::Main(int argc, char** argv) +{ + if(argc==0) + { + PrintUsage(); + } + const char* command = argv[0]; + if(argc == 6 && !strcmp(command,"Absorption")) + { + int aInfiniteB; + double aEmax; + double aSpecAlpha; + int aEBLmodel; + double aDistanceMpc; + int par=1; + if(sscanf(argv[par++], "%d", &aInfiniteB)!=1) + Exception::Throw("Failed to read InfiniteB"); + if(sscanf(argv[par++], "%lg", &aSpecAlpha)!=1) + Exception::Throw("Failed to read SpecAlpha"); + if(sscanf(argv[par++], "%d", &aEBLmodel)!=1) + Exception::Throw("Failed to read EBLmodel"); + if(sscanf(argv[par++], "%lg", &aEmax)!=1) + Exception::Throw("Failed to read Emax"); + if(sscanf(argv[par++], "%lg", &aDistanceMpc)!=1) + Exception::Throw("Failed to read DistanceMpc"); + AbsorptionTest(aInfiniteB!=0, aSpecAlpha, aEBLmodel, aEmax*1e-6/units.Eunit, aDistanceMpc); + } + else if(argc == 5 && !strcmp(command,"DiffuseToPoint")) + { + double aSpecAlpha; + int aEBLmodel; + double aEmax; + int par=1; + int aEvolutionModel = -1; + //if(sscanf(argv[par++], "%d", &aInfiniteB)!=1) + // Exception::Throw("Failed to read InfiniteB"); + if(sscanf(argv[par++], "%lg", &aSpecAlpha)!=1) + Exception::Throw("Failed to read SpecAlpha"); + if(sscanf(argv[par++], "%d", &aEBLmodel)!=1) + Exception::Throw("Failed to read EBLmodel"); + if(sscanf(argv[par++], "%lg", &aEmax)!=1) + Exception::Throw("Failed to read Emax"); + if(sscanf(argv[par++], "%d", &aEvolutionModel)!=1) + Exception::Throw("Failed to read EvolutionModel"); + DiffuseToPointTest(aSpecAlpha, aEBLmodel, aEmax*1e-6/units.Eunit, aEvolutionModel); + } + else + PrintUsage(); +} + +void FermiTests::PrintUsage() +{ + cerr << "FERMI test commands syntax:" << std::endl << + "Absorption InfiniteB SpecAlpha EBLmodel Emax/eV DistanceMpc" << std::endl << "\tor" << std::endl << + "DiffuseToPoint SpecAlpha EBLmodel Emax/eV EvolutionModel" << std::endl; +} + +void FermiTests::InitCosmology() +{ + if(!cosmology.IsInitialized()) + cosmology.Init(); +} + +void FermiTests::InitSpec(int nParticles, ParticleStack& aStack, double aSpecAlpha, double aEmin, double aEmax, double aZ, double aWeight, Result* aInitialOutput) +{ + double logStep = pow(aEmax/aEmin, 1./(double)nParticles); + Particle gamma(Photon, aZ); + double logScaleAlpha = aSpecAlpha - 1.; + double E = aEmin*logStep; + for(int i=0; i<nParticles; i++, E*=logStep) + { + gamma.Energy = E; + gamma.Weight = aWeight*pow(E/aEmin, -logScaleAlpha); + aStack.AddPrimary(gamma); + if(aInitialOutput) + aInitialOutput->Add(gamma); + } +} + +Particle FermiTests::SamplePowerLawSpec(double aSpecAlpha, double aEmin, double aEmax, double aZ, double aWeight, double aRand) +{ + double logScaleAlpha = aSpecAlpha - 1.; + double E = aEmin * pow(aEmax/aEmin, aRand);//sampling uniform in logscale + Particle gamma(Photon, aZ); + gamma.Energy = E; + gamma.Weight = aWeight*pow(E/aEmin, -logScaleAlpha);//power law spectrum + return gamma; +} + +IBackground* FermiTests::CreateEBL(int aEBLmodel) +{ + IBackground* b2 = 0; + if(aEBLmodel==8) + b2 = new Kneiske1001EBL(); + else if(aEBLmodel==9) + b2 = new Kneiske0309EBL(); + else if(aEBLmodel!=0) + Exception::Throw("Unsupported EBLmodel parameter: expected values 0-None, 8-minimal, 9-best fit"); + return b2; +} + +const double FermiTests::MinE = 10000;//MeV + +void FermiTests::AbsorptionTest(bool aInfiniteB, double aSpecAlpha, int aEBLmodel, double aEmax, double aDistanceMpc) +{ + int nParticles = 30000; + InitCosmology(); + double aZmax = cosmology.d2z(aDistanceMpc*units.Mpc); + + double EminOutput = MinE/units.Eunit; + double EminCalc = 0.99*EminOutput; + double alphaThinning = 0.5;//alpha = 1 conserves number of particles on the average; alpha = 0 disables thinning + ParticleStack particles; + + std::string modelStr = ToString(aDistanceMpc) + "Mpc_B" + (aInfiniteB ? "1" : "0") + + + "_Emax" + ToString(aEmax/units.eV) + "_Alpha" + ToString(aSpecAlpha); + Result resultFin(EminCalc); + resultFin.AddOutput(new SpectrumOutput(modelStr + "_final_spec" , EminOutput, pow(10,0.05))); + SmartPtr<IntegralSpectrumOutput> finalIntSpec = new IntegralSpectrumOutput(modelStr + "_final_int_spec", 100./units.Eunit); + resultFin.AddOutput(finalIntSpec); + + Result resultIni(EminCalc); + resultIni.AddOutput(new SpectrumOutput(modelStr + "_ini_spec" , EminOutput, pow(10,0.05))); + SmartPtr<IntegralSpectrumOutput> iniIntSpec = new IntegralSpectrumOutput(modelStr + "_ini_int_spec", 100./units.Eunit); + resultIni.AddOutput(iniIntSpec); + + PropagationEngine pe(particles, resultFin); + EnergyBasedThinning thinning(alphaThinning); + InitPropagEngine(pe, aInfiniteB, aEBLmodel, &thinning, aZmax, EminCalc); + + InitSpec(nParticles, particles, aSpecAlpha, EminOutput, aEmax, aZmax, 1., &resultIni); + //Randomizer r; + //for(; nParticles>0; nParticles--) + //{ + // Particle p = SamplePowerLawSpec(aSpecAlpha, EminOutput, aEmax, aZmax, 1., r.Rand()); + // particles.Add(p); + // resultIni.Add(p); + //} + + + resultIni.Flush(); + + //main calculation cycle + pe.RunMultithread(); + //pe.Run(); + + //output integral and diff spectra + resultFin.Flush(); + + //output absorption factor + SmartPtr<TableFunction> fIni = iniIntSpec->IntegralSpectrum(); + SmartPtr<TableFunction> fFin = finalIntSpec->IntegralSpectrum(); + double step = pow(10,0.05); + std::ofstream absOut; + absOut.open((modelStr + "_abs").c_str(),std::ios::out); + + //fIni->Print(std::cout); + + double aEmaxeV = aEmax*1e6*units.Eunit; + for(double E=fIni->Xmin(); E<=fIni->Xmax(); E*=step) + { + double initialF = fIni->f(E); + ASSERT(initialF>=0); + if(initialF==0.) + continue; + double absorbtion = fFin->f(E)/fIni->f(E); + absOut << aEmaxeV << "\t" << aEBLmodel << + "\t" << aSpecAlpha << "\t" << (aInfiniteB?1:0) << + "\t" << aDistanceMpc << "\t" << E << "\t" << absorbtion << std::endl; + } + absOut.close(); +} + +void FermiTests::InitPropagEngine(PropagationEngine& pe, bool aInfiniteB, int aEBLmodel, IThinning* aThinning, double aZmax, double EminCalc) +{ + bool splitted = false; + double logStepK = pow(10,0.05); + CompoundBackground backgr; + + double cmbTemp = 2.73/Units::phTemperature_mult/units.Eunit; + IBackground* b1 = new PlankBackground(cmbTemp, 1e-3*cmbTemp, 1e3*cmbTemp); + IBackground* b2 = CreateEBL(aEBLmodel); + + backgr.AddComponent(b1);//transfers ownership + if(b2) + backgr.AddComponent(b2); + + double stepZ = aZmax<0.2 ? aZmax/2 : 0.1; + double epsRel = 1e-3; + + PBackgroundIntegral backgrI = new ContinuousBackgroundIntegral(backgr, stepZ, logStepK, aZmax, epsRel); + PBackgroundIntegral backgrI1 = new ContinuousBackgroundIntegral(*b1, stepZ, logStepK, aZmax, epsRel); + PBackgroundIntegral backgrI2 = b2?new ContinuousBackgroundIntegral(*b2, stepZ, logStepK, aZmax, epsRel):0; + + + pe.SetThinning(aThinning); + pe.AddInteraction(new RedShift()); + + if(splitted) + { + if(!aInfiniteB) + { + pe.AddInteraction(new Interactions::ICS(backgrI1,EminCalc)); + pe.AddInteraction(new Interactions::IcsCEL(backgrI1,EminCalc)); + if(b2) + { + pe.AddInteraction(new Interactions::ICS(backgrI2,EminCalc)); + pe.AddInteraction(new Interactions::IcsCEL(backgrI2,EminCalc)); + } + } + pe.AddInteraction(new Interactions::GammaPP(backgrI1)); + if(b2) + pe.AddInteraction(new Interactions::GammaPP(backgrI2)); + } + else + { + if(!aInfiniteB) + { + pe.AddInteraction(new Interactions::ICS(backgrI,EminCalc)); + pe.AddInteraction(new Interactions::IcsCEL(backgrI,EminCalc)); + } + pe.AddInteraction(new Interactions::GammaPP(backgrI)); + } +} + +void FermiTests::DiffuseToPointTest(double aSpecAlpha, int aEBLmodel, double aEmax, int aEvolutionModel) +{ + time_t tStart = time(0); + int nTotalParticles = 30000; + double ZmaxFull = 3; + double ZmaxPoint = 0.06; + + InitCosmology(); + double EminOutput = MinE/units.Eunit; + double EminCalc = 0.99*EminOutput; + double alphaThinning = 0.5;//alpha = 1 conserves number of particles on the average; alpha = 0 disables thinning + ParticleStack particlesPointB0; + ParticleStack particlesPointB1; + ParticleStack particlesFull; + + std::string modelStr = //ToString(aInfiniteB ? "B1_" : "B0_") + + ToString("Emax") + ToString(aEmax/units.eV) + "_Alpha" + ToString(aSpecAlpha) + "_Evol" + ToString(aEvolutionModel); + Result resultFull(EminCalc); + resultFull.AddOutput(new SpectrumOutput(modelStr + "_full_spec" , EminOutput, pow(10,0.05))); + SmartPtr<IntegralSpectrumOutput> fullIntSpec = new IntegralSpectrumOutput(modelStr + "_full_int_spec", 100./units.Eunit); + resultFull.AddOutput(fullIntSpec); + + Result resultPointB0(EminCalc); + resultPointB0.AddOutput(new SpectrumOutput(ToString("B0_") + modelStr + "_point_spec" , EminOutput, pow(10,0.05))); + SmartPtr<IntegralSpectrumOutput> pointB0IntSpec = new IntegralSpectrumOutput(ToString("B0_") + modelStr + "_point_int_spec", 100./units.Eunit); + resultPointB0.AddOutput(pointB0IntSpec); + + Result resultPointB1(EminCalc); + resultPointB1.AddOutput(new SpectrumOutput(ToString("B1_") + modelStr + "_point_spec" , EminOutput, pow(10,0.05))); + SmartPtr<IntegralSpectrumOutput> pointB1IntSpec = new IntegralSpectrumOutput(ToString("B1_") + modelStr + "_point_int_spec", 100./units.Eunit); + resultPointB1.AddOutput(pointB1IntSpec); + + PropagationEngine pePointB0(particlesPointB0, resultPointB0); + PropagationEngine pePointB1(particlesPointB1, resultPointB1); + PropagationEngine peFull(particlesFull, resultFull); + EnergyBasedThinning thinning(alphaThinning); + InitPropagEngine(peFull, false, aEBLmodel, &thinning, ZmaxFull, EminCalc); + InitPropagEngine(pePointB0, false, aEBLmodel, &thinning, ZmaxFull, EminCalc); + InitPropagEngine(pePointB1, true, aEBLmodel, &thinning, ZmaxFull, EminCalc); + + int nEnergyBins=100; + int nParticlesClose = nTotalParticles/2; + int nParticlesFar = nTotalParticles-nParticlesClose; + + double tEarliest = cosmology.z2t(ZmaxFull); + double t0 = cosmology.z2t(0.); + double tPoint = cosmology.z2t(ZmaxPoint); + + double weightFar = (tPoint-tEarliest)/(t0-tPoint)*nParticlesClose/nParticlesFar; + + Randomizer r; + for(int nSources = nParticlesClose/nEnergyBins; nSources>0; nSources--) + {//sampling close sources + double t = tPoint + r.Rand()*(t0-tPoint); + double z = cosmology.t2z(t); + double weight = Evolution::EvolFactor(z, aEvolutionModel); + InitSpec(nEnergyBins, particlesPointB0, aSpecAlpha, EminOutput, aEmax, z, weight); + InitSpec(nEnergyBins, particlesPointB1, aSpecAlpha, EminOutput, aEmax, z, weight); + InitSpec(nEnergyBins, particlesFull, aSpecAlpha, EminOutput, aEmax, z, weight); + } + for(int nSources = nParticlesFar/nEnergyBins; nSources>0; nSources--) + {//sampling far away sources + double t = tEarliest + r.Rand()*(tPoint-tEarliest); + double z = cosmology.t2z(t); + double weight = Evolution::EvolFactor(z, aEvolutionModel)*weightFar; + InitSpec(nEnergyBins, particlesFull, aSpecAlpha, EminOutput, aEmax, z, weight); + } + + std::cerr << "\n starting B0 calculation on " << time(0) - tStart << " sec" << std::endl; + + //calculate point source spectrum + pePointB0.RunMultithread(); + resultPointB0.Flush(); + + std::cerr << "\n starting B1 calculation on " << time(0) - tStart << " sec" << std::endl; + + //calculate point source spectrum + pePointB1.RunMultithread(); + resultPointB1.Flush(); + + std::cerr << "\n starting diffuse calculation on " << time(0) - tStart << " sec" << std::endl; + + //calculate full spectrum + peFull.RunMultithread(); + resultFull.Flush(); + + std::cerr << "\n starting diffuse to point ratio calculation on " << time(0) - tStart << " sec" << std::endl; + + //output diffuse to point fraction + SmartPtr<TableFunction> fPointB0 = pointB0IntSpec->IntegralSpectrum(); + SmartPtr<TableFunction> fPointB1 = pointB1IntSpec->IntegralSpectrum(); + SmartPtr<TableFunction> fFull = fullIntSpec->IntegralSpectrum(); + double step = pow(10,0.05); + double aEmaxeV = aEmax*1e6*units.Eunit; + TableFunction* intFpoint[2] = {fPointB0, fPointB1}; + for(int i=0; i<2; i++) + { + std::ofstream ratioOut; + ratioOut.open((ToString("B") + ToString(i) + "_" + modelStr + "_diff_to_point").c_str(),std::ios::out); + TableFunction* fPoint = intFpoint[i]; + for(double E=fPoint->Xmin(); E<=fPoint->Xmax(); E*=step) + { + double pointF = fPoint->f(E); + ASSERT(pointF>=0); + if(pointF==0.) + continue; + double ratio = (fFull->f(E)-pointF)/pointF; + ratioOut << E << "\t" << ratio << "\t" << aEmaxeV << "\t" << aEBLmodel << + "\t" << aSpecAlpha << "\t" << 0 << std::endl; + } + ratioOut.close(); + } + std::cerr << "\n finished on " << time(0) - tStart << " sec" << std::endl; +} + +double Evolution::EvolFactor(double z, int aEvolutionModel) +{ + switch (aEvolutionModel) + { + case EZDependenceOff: + return 1.; + case WaxmanBahcall://if m=0 corresponds to Waxman-Bahcall, hep-ph/9807282; Engel at al astro-ph/0101216 (oSFR in astro-ph/0605327v2) + return WaxmanBahcallEvolution(z); + case SFR03://if m=0 corresponds to Star Formation Rate from astro-ph/0309141 + return SfrEvolution(z); + case SFR06://if m=0 corresponds to star formation rate from astro-ph/0607087 (lower figure 3) + return SfrNewEvolution(z); + case Weak://if m=0 corresponds to m=3 to z=1.8 and constant to z=3 going to zero there + return weakEvolution(z); + case Baseline://if m=0 corresponds to baseline evolution astro-ph-1512479 + return baselineEvolution(z); + case Strong://if m=0 corresponds to fast evolution astro-ph-1512479 + return strongEvolution(z); + case Engel9://if m=0 corresponds to Fig. 9 of http://lanl.arxiv.org/abs/astro-ph/0101216v2 + return Engel9Evolution(z); + case SFR_Yuksel: + return SFR_YukselEvolution(z); + case GRB_Yuksel: + return GRB_YukselEvolution(z); + case AGN_Ahlers: + return AGN_AhlersEvolution(z); + default: + Exception::Throw("invalid value of EvolutionModel parameter"); + break; + }; + return 0.; +} + +FermiTests::IntegralSpectrumOutput::IntegralSpectrumOutput(std::string aFile, double aEmin): + fEmin(aEmin) +{ + fOut.open(aFile.c_str(),std::ios::out); + if(!fOut.is_open()) + Exception::Throw("Failed to open " + aFile); +} + +FermiTests::IntegralSpectrumOutput::~IntegralSpectrumOutput() { + if(fOut.is_open()) + fOut.close(); +} + +void FermiTests::IntegralSpectrumOutput::AddParticle(const Particle& aParticle) +{ + if(aParticle.Energy>=fEmin && aParticle.Type == Photon) + fParticles.insert(aParticle); +} + +void FermiTests::IntegralSpectrumOutput::Flush(bool aIsFinal) +{ + if(!aIsFinal) + return;//intermediate output is not supported + if(!fOut.is_open()) + { + std::cerr << "FermiTests::IntegralSpectrumOutput::Flush(): ignoring second call"; + return; + } + double E=-1; + double sum=0.; + double mult = units.Eunit*1e6; + for(multiset<Particle,More>::iterator it = fParticles.begin(); it != fParticles.end(); it++) + { + const Particle& p = *it; + if(p.Energy != E && sum > 0) + { + fOut << E*mult << "\t" << sum*mult << "\n"; + fE.insert(fE.begin(), E*mult); + fIntFlux.insert(fIntFlux.begin(), sum*mult); + } + E = p.Energy; + sum += p.Weight; + } + if(sum>0) + { + fOut << E*mult << "\t" << sum*mult << "\n"; + fE.insert(fE.begin(), E*mult); + fIntFlux.insert(fIntFlux.begin(), sum*mult); + } + fOut.close(); +} + +TableFunction* FermiTests::IntegralSpectrumOutput::IntegralSpectrum() const +{ + ASSERT(fE.size()>0);//End must be called prior to this method call + ASSERT(fE.size()>=2);//at least two data points should be present + return new LinearFunc(fE, fIntFlux); +} + +void TestList::RegisterTest(std::string aKey, TestProc aFunc) { + fTests[aKey] = aFunc; +} + +TestList::TestList() { + TestList::RegisterTest("TurbulentMF",TurbulentMF::UnitTest); + TestList::RegisterTest("MonochromaticMF",MonochromaticMF::UnitTest); + TestList::RegisterTest("Deflection3D",Deflection3D::UnitTest); + TestList::RegisterTest("NeutronDecay",NeutronDecay::UnitTestEconserv); + TestList::RegisterTest("Stecker05EBL",Stecker2005EBL::UnitTest); + TestList::RegisterTest("Stecker16EBL",Stecker16Background::UnitTest); + TestList::RegisterTest("nucl_dis",PhotoDisintegration::UnitTest); + TestList::RegisterTest("sampler",MathUtils::UnitTest); +} + + +EConservationTest::EConservationTest(double aMaxZ, double Emin_eV, u_long aRandSeed): +randomizer(aRandSeed), +result(Emin_eV*units.eV), +primary(Electron,0), +alphaThinning(0) +{ + if(!cosmology.IsInitialized()) + cosmology.Init(); + + out = new TotalEnergyOutput(); + result.AddOutput(out); + + double cmbTemp = 2.73*units.K; + backgr.AddComponent(new PlankBackground(cmbTemp, 1e-3*cmbTemp, 1e3*cmbTemp)); + backgr.AddComponent(new Backgrounds::Kneiske1001EBL()); + + double logStepK = pow(10,0.05); + double epsRel = 1e-3; + + stepZ = aMaxZ <0.2 ? aMaxZ /2 : 0.1; + backgrI = new ContinuousBackgroundIntegral(backgr, stepZ, logStepK, aMaxZ, epsRel); + pe = new PropagationEngine(particles, result, randomizer.CreateIndependent()); +} + +void EConservationTest::SetPrimary(const Particle& aPrimary, int aCount) +{ + ASSERT(nParticles>0); + primary = aPrimary; + for(int i=0; aCount > i; i++) + { + particles.AddPrimary(aPrimary); + } + nParticles = aCount; +} + +void EConservationTest::Run() +{ + ASSERT(nParticles>0);//SetPrimary should be called prior to this method + EnergyBasedThinning thinning(alphaThinning); + pe->SetThinning(&thinning); + pe->Run(); + + double startE = nParticles* primary.Energy; + double Ep = out->TotalE(primary.Type); + double Esec = 0; + for(int i=0; i<ParticleTypeEOF; i++) + if(i!= primary.Type) + Esec += out->TotalE((ParticleType)i); + double deltaEp = startE-Ep; + std::cout << "|deltaE_prim|/E_prim = " << deltaEp/startE << "\t\t(|deltaE_prim|-E_sec)/|deltaE_prim| = " << (deltaEp-Esec)/deltaEp << std::endl; +} + + +} /* namespace Test */ diff --git a/src/lib/Test.h b/src/lib/Test.h new file mode 100644 index 0000000..bdbe784 --- /dev/null +++ b/src/lib/Test.h @@ -0,0 +1,232 @@ +/* + * Test.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#ifndef TEST_H_ +#define TEST_H_ + +#include <string> +#include <map> +#include "Background.h" +#include "ParticleStack.h" +#include "PropagationEngine.h" + +namespace Test { + +using namespace mcray; + typedef int (*TestProc)(); + +class TestList { +public: + + TestList(); + + int Main(int argc, char** argv); + static void FrameworkTest(); + static void BackgroundTest(); + static void BackgroundTest(const IBackground& aBackground, std::ostream& aOut); + static void SplittedBackgroundTest(bool aSplitted); + static void ThinningTest(double aAlpha); + static void KachelriesTest(double aEmin_eV, double aEmax_eV, double aZmax, + double aAlpha, bool aSplitted, int aNoParticles, + bool aNoPP, bool aNoICS, bool aNoICScel, bool aNoRedshift, + ParticleType aPrimary, const char* aOutputDir = 0); + static void MonochromaticAndGausianBackgroundTest(bool aMonochromatic, bool aICS, bool aICScel, bool aPP); + static void MultithreadTest(bool aMultithread); + static void BichromaticTest(bool aGauss); + static void SamplingTest(); + static void EngineTest(double aAlphaThinning); + void RegisterTest(std::string aKey, TestProc aFunc); +private: + std::map<std::string, TestProc> fTests; +}; + +class FermiTests { + class IntegralSpectrumOutput : public TSmartReferencedObj<IOutput> { + struct More : public std::binary_function<Particle, Particle, bool> + { + bool + operator()(const Particle& __x, const Particle& __y) const + { + return __x.Energy > __y.Energy; + } + }; + std::multiset<Particle,More> fParticles;//sort by energy in descending order + public: + IntegralSpectrumOutput(std::string aFile, double aEmin); + virtual ~IntegralSpectrumOutput(); + void AddParticle(const Particle& aParticle); + void Flush(bool aIsFinal); + TableFunction* IntegralSpectrum() const;//may only be called after End() + private: + std::ofstream fOut; + double fEmin; + std::vector<double> fE; + std::vector<double> fIntFlux; + }; +public: + static void Main(int argc, char** argv); +private: + static void InitCosmology(); + static void PrintUsage(); + static void AbsorptionTest(bool aInfiniteB, double aSpecAlpha, int aEBLmodel, double aEmax, double aDistanceMpc); + static void DiffuseToPointTest(double aSpecAlpha, int aEBLmodel, double aEmax, int aEvolutionModel); + static void InitSpec(int aParticlesCount, ParticleStack& aStack, double aSpecAlpha, double aEmin, double aEmax, double aZ, double aWeight, Result* aInitialOutput = 0); + static Particle SamplePowerLawSpec(double aSpecAlpha, double aEmin, double aEmax, double aZ, double aWeight, double aRand); + static IBackground* CreateEBL(int aEBLmodel); + static void InitPropagEngine(PropagationEngine& aEngine, bool aInfiniteB, int aEBLmodel, IThinning* aThinning, double aZmax, double aEminCalc); + static const double MinE; +}; + +class Evolution +{ +public: + enum TZDependence{ + EZDependenceOff = 0,//(1+z)^3 + EZDependencePowerLaw,//(1+z)^(3+m) + EZDependencePowerTD,//t^{-4+p} + + //injection spectrum types below are proportional to (1+z)^m: + + WaxmanBahcall,//if m=0 corresponds to Waxman-Bahcall, hep-ph/9807282; Engel at al astro-ph/0101216 (oSFR in astro-ph/0605327v2) + SFR03,//if m=0 corresponds to Star Formation Rate from astro-ph/0309141 + Weak,//if m=0 corresponds to m=3 to z=1.8 and constant to z=3 going to zero there + Baseline,//if m=0 corresponds to baseline evolution astro-ph-1512479 + Strong,//if m=0 corresponds to fast evolution astro-ph-1512479 + Engel9,//if m=0 corresponds to Fig. 9 of http://lanl.arxiv.org/abs/astro-ph/0101216v2 + PowerCut,//(1+z)^m from Zmin up to PowerCutZ and const from PowerCutZ to Zmax + SFR06,//star formation rate from astro-ph/0607087 (lower figure 3) + SFR_Yuksel,//star formation rate from http://lanl.arxiv.org/abs/1103.3574v2 (formula 11) + GRB_Yuksel,//GRB rate from http://lanl.arxiv.org/abs/1103.3574v2 (formula 12) + AGN_Ahlers,//AGN rate from http://lanl.arxiv.org/abs/1103.3574v2 (formula 13) + EZDependenceEOF + }; + + static double EvolFactor(double aZ, int aEvolutionModel); + + static inline double weakEvolution(double aZ){//corresponds to comoving frame only if m=0!!! + //m=3 to z=1.8 and constant to z=3 going to zero there + return (aZ<1.8)?pow(1.+aZ,3.):((aZ<3.)?21.952:0.); + } + + //baseline evolution astro-ph-0512479 + static inline double baselineEvolution(double aZ){//corresponds to comoving frame only if m=0!!! + //m=4 to z=1 and then constant to z=6 going to zero there. + return (aZ<1.3)?pow(1.+aZ,3.1):((aZ<6)?13.2238005912547:0.); + } + + //fast evolution astro-ph-0512479 + static inline double strongEvolution(double aZ){//corresponds to comoving frame only if m=0!!! + //m=4 to z=1 and then constant to z=6 going to zero there. + return (aZ<1.0)?pow(1.+aZ,4.):((aZ<6)?16.:0.); + } + + //AGN + //Waxman-Bahcall, hep-ph/9807282; Engel at al astro-ph/0101216 (oSFR in astro-ph/0605327v2) + static inline double WaxmanBahcallEvolution(double aZ){//corresponds to comoving frame only if m=0!!! + return (aZ<1.9)?pow(1.+aZ,3.):(24.389*((aZ<2.7)?1.:exp((2.7-aZ)/2.7))); + } + + //Fig. 9 of http://lanl.arxiv.org/abs/astro-ph/0101216v2 + static inline double Engel9Evolution(double aZ){//corresponds to comoving frame only if m=0!!! + return ((aZ<1.9)?pow(1.+aZ,4.):70.7281); + } + + //Star Formation Rate from astro-ph/0309141 + static inline double SfrEvolution(double aZ){//corresponds to comoving frame only if m=0!!! + return (aZ<1.2)?pow(1.+aZ, 3.5):40.68052976*pow(1.+aZ, -1.2);//40.68052976==(1+1.2)^(3.5+1.2) + } + + //star formation rate from astro-ph/0607087 (lower figure 3) + static inline double SfrNewEvolution(double aZ){//corresponds to comoving frame only if m=0!!! + if(aZ<1.) + return pow(1.+aZ, 4.65); + else if(aZ<3.) + return 7.7274906314*pow(1.+aZ, 1.7); + else if(aZ<6.) + return 7912.950406552335*pow(1.+aZ, -3.3); + else if(aZ<8.) + return 606880463687.06*pow(1.+aZ, -12.63); + else + return 0.; + } + + //star formation rate from http://lanl.arxiv.org/abs/1103.3574v2 (formula 11) + static inline double SFR_YukselEvolution(double aZ){//corresponds to comoving frame only if m=0!!! + if(aZ<1.) + return pow(1.+aZ, 3.4); + else if(aZ<4.) + return 10.5560632861832*pow((1.+aZ)/2., -0.3); + else + return 8.01899573803639*pow((1.+aZ)/5., -3.5); + } + + //GRB rate from http://lanl.arxiv.org/abs/1103.3574v2 (formula 12) + static inline double GRB_YukselEvolution(double aZ){//corresponds to comoving frame only if m=0!!! + if(aZ<1.) + return pow(1.+aZ, 4.8); + else if(aZ<4.) + return 27.857618025476*pow((1.+aZ)/2., 1.1); + else + return 76.3269641062938*pow((1.+aZ)/5., -2.1); + } + + //AGN rate from http://lanl.arxiv.org/abs/1103.3574v2 (formula 13) + static inline double AGN_AhlersEvolution(double aZ){//corresponds to comoving frame only if m=0!!! + if(aZ<1.7) + return pow(1.+aZ, 5); + else if(aZ<2.7) + return 143.48907; + else + return 143.48907*pow(10.,2.7-aZ); + } +}; + + class EConservationTest{ + public: + EConservationTest(double aMaxZ, double Emin_eV=1e6, u_long aRandSeed = 0); + void SetPrimary(const Particle& aPrimary, int aCount); + void Run(); + Randomizer& GetRandomizer() { return randomizer;} + PropagationEngine& Engine(){return *pe;} + BackgroundIntegral* Backgr(){ return backgrI;} + double alphaThinning;//alpha = 1 conserves number of particles on the average; alpha = 0 disables thinning + private: + Randomizer randomizer; + ParticleStack particles; + Result result; + SmartPtr<TotalEnergyOutput> out; + CompoundBackground backgr; + double stepZ; + std::vector<PInteraction> fInteractions; + PBackgroundIntegral backgrI; + SafePtr<PropagationEngine> pe; + Particle primary; + int nParticles; + }; +} /* namespace Test */ +#endif /* TEST_H_ */ diff --git a/src/lib/Thinning.cpp b/src/lib/Thinning.cpp new file mode 100644 index 0000000..2a4df82 --- /dev/null +++ b/src/lib/Thinning.cpp @@ -0,0 +1,65 @@ +/* + * Thinning.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#include "Thinning.h" + +namespace mcray +{ + +EnergyBasedThinning::EnergyBasedThinning(double aAlpha, FilterMode aFilterMode) : + fAlpha(aAlpha), + fEnableFor(ParticleTypeEOF, aFilterMode != BlackList) +{ + ASSERT(aAlpha>=0 && aAlpha<=1); +} + +void EnergyBasedThinning::Run(std::vector<Particle>& aParticles, Randomizer& aRandomizer) +{ + if(aParticles.size()<=1) + return; + double totEnergy = 0.; + for(std::vector<Particle>::const_iterator it = aParticles.begin(); it != aParticles.end(); it++) + if(fEnableFor[it->Type]) + totEnergy += it->Energy; + if(totEnergy == 0.) + return;//preserve all particles + for(int i=aParticles.size()-1; i>=0; i--) + { + if(!fEnableFor[aParticles[i].Type]) + continue; + double probability = pow(aParticles[i].Energy/totEnergy, fAlpha);//probability to survive + if(aRandomizer.Rand()>probability) { + LOG_VERBOSE("thinning:erase\t" + (aParticles.begin() + i)->ToString()); + aParticles.erase(aParticles.begin() + i); + } + else + aParticles[i].Weight /= probability; + } +} + +}//end of namespace mcray diff --git a/src/lib/Thinning.h b/src/lib/Thinning.h new file mode 100644 index 0000000..1b87178 --- /dev/null +++ b/src/lib/Thinning.h @@ -0,0 +1,63 @@ +/* + * Thinning.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#ifndef THINNING_H +#define THINNING_H + +#include <vector> +#include "Particle.h" +#include "Randomizer.h" + +namespace mcray +{ + +class IThinning { +public: + //must be thread-safe + virtual void Run(std::vector<Particle>& aParticles, Randomizer& aRandomizer) = 0; +}; + + enum FilterMode{ + BlackList, + WhiteList + }; + +class EnergyBasedThinning : public IThinning { +public: + EnergyBasedThinning(double aAlpha, FilterMode aFilterMode=WhiteList); + void Run(std::vector<Particle>& aParticles, Randomizer& aRandomizer); + virtual ~EnergyBasedThinning(){} + void EnableFor(ParticleType aType, bool aDoEnable = true) { fEnableFor[aType] = aDoEnable;} +private: + double fAlpha; + std::vector<bool> fEnableFor; +}; + +} +#endif /* THINNING_H */ + diff --git a/src/lib/Units.cpp b/src/lib/Units.cpp new file mode 100644 index 0000000..3026d79 --- /dev/null +++ b/src/lib/Units.cpp @@ -0,0 +1,92 @@ +/* + * Units.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#include "Units.h" +#include <math.h> + +namespace mcray +{ +const double Units::e = 0.0854245431348626; /* sqrt(alpha) */ +const double Units::plank=6.582122020e-22; /* Plank constant (MeV*s) */ +const double Units::plank_ESU=1.05457266e-27;/* Plank constant (erg*s) */ +const double Units::phTemperature_mult=1.16e10;// K/MeV +const double Units::Mpc_in_cm=3.0856775807e24; /* (cm) */ +#ifdef ELMAG_TEST +const double Units::lightspeed=3e10; /* (cm/s) */ +#else +const double Units::lightspeed=2.99792458e10; /* (cm/s) */ +#endif +const double Units::MeVinESU=1.60217733e-6; /* 1MeV in Ergs */ +const double Units::YearInSec = 31557600;//number of seconds in 1 year + +const Units units;//global units object + +Units::Units() +{ + Eunit=1.; /* units of energy used (in MeV) */ //TODO: BUG!!! setting Eunit=1e-6 lead to different results: try for ex --test FERMI Absorption 1 2 8 1e15 0.06 + Eunit3=0; /* Eunit^3 */ + Lunit=0; /* length unit in sm. */ + Tunit=0; /* time unit in sec. */ + Vunit=0; /* Vunit=Lunit^3 */ + sigmaUnit=0; /* sigmaUnit=Lunit^2 */ + SpecUnit=0; /* SpecUnit=Eunit*Tunit*Vunit */ + outEunit=1e-6; /* Output data energy unit in MeV*/ + Bunit=0; /* Magnetic field internal unit in Gauss (esu) */ + barn = 0;/* 1 barn (cross-section unit) in internal units */ + + MeV = 1./Eunit; + K = MeV/phTemperature_mult; + eV = 1e-6*MeV; + GeV = 1e3*MeV; + TeV = 1e6*MeV; + PeV = 1e9*MeV; + Tunit=plank/Eunit; /* time unit in sec. */ + Lunit=lightspeed*Tunit; /* length unit in sm. */ + Eunit3 = Eunit*Eunit*Eunit; + + sigmaUnit=Lunit*Lunit; + Vunit=Lunit*Lunit*Lunit; + SpecUnit=Eunit*Tunit*Vunit; + + Bunit=MeVinESU*MeVinESU*pow(plank_ESU*lightspeed,-1.5); // 1MeV^2 in Gauss + Bunit*=(Eunit*Eunit); //B unit in Gauss (esu) + Gauss=1./Bunit; + barn = 1e-24/sigmaUnit; + Mpc = Mpc_in_cm/Lunit; + kpc = 1e-3*Mpc; + second = 1./Tunit; + year = YearInSec * second; + cm = 1./Lunit; + cm3 = cm*cm*cm; + Hz_photon = 2.*M_PI/second; + erg = MeV/MeVinESU; + J = (1e7*erg); + W = (J/second); +} + +} /* namespace mcray */ diff --git a/src/lib/Units.h b/src/lib/Units.h new file mode 100644 index 0000000..2db4f59 --- /dev/null +++ b/src/lib/Units.h @@ -0,0 +1,83 @@ +/* + * Units.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#ifndef UNITS_H_ +#define UNITS_H_ + +namespace mcray +{ + +class Units +{ +public: + Units(); + static const double Mpc_in_cm; /* (cm) */ + static const double lightspeed; /* (cm/s) */ + static const double e; /* electric charge of electron in system plank=c=1*/ + + double Eunit; /* units of energy used (in MeV) */ + double Eunit3; /* Eunit^3 */ + double Lunit; /* length unit in cm. */ + double Tunit; /* time unit in sec. */ + double Vunit; /* Vunit=Lunit^3 */ + double sigmaUnit; /* sigmaUnit=Lunit^2 */ + double SpecUnit; /* SpecUnit=Eunit*Tunit*Vunit */ + double outEunit; /* Output data energy unit in MeV*/ + double Bunit; /* Magnetic field internal unit in Gauss (esu) */ + static const double MeVinESU; /* 1MeV in Ergs */ + static const double YearInSec;//number of seconds in 1 year + static const double phTemperature_mult; + +////// conventional units expressed in internal units //////////////// + + double Mpc; + double kpc; + double barn; + double MeV; + double GeV; + double eV; + double TeV; + double PeV; + double second; + double year; + double cm; + double cm3;//cubic cm + double erg; + double K;//Kelvin + double Gauss; + + double Hz_photon;// energy of 1 Hz EM wave photon (in internal units) + double J; //Joule + double W; //Watt +private: + static const double plank; /* Plank constant (MeV*s) */ + static const double plank_ESU;/* Plank constant (erg*s) */ +}; + extern const Units units; +} /* namespace mcray */ +#endif /* UNITS_H_ */ diff --git a/src/lib/Utils.cpp b/src/lib/Utils.cpp new file mode 100644 index 0000000..3d3f0dd --- /dev/null +++ b/src/lib/Utils.cpp @@ -0,0 +1,32 @@ +/* + * Utils.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#include "Utils.h" + + + diff --git a/src/lib/Utils.h b/src/lib/Utils.h new file mode 100644 index 0000000..3e8651a --- /dev/null +++ b/src/lib/Utils.h @@ -0,0 +1,234 @@ +/* + * Utils.h + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#ifndef UTILS_H +#define UTILS_H + +#include <string> +#include <sstream> +#include <vector> +#include <float.h> +#include <iostream> +#include <iomanip> +#include <omp.h> +#include <sys/types.h> +#include "Debug.h" +#include <algorithm> + +namespace Utils { + +#define DIR_DELIMITER_STR "/" +#define TABLES_DIR "tables" DIR_DELIMITER_STR + +class Exception +{ +public: + Exception(std::string aErrorMessage):fMessage(aErrorMessage){} + std::string Message() const { return fMessage; } + inline static void Throw(std::string aErrorMessage) { throw new Exception(aErrorMessage); } +private: + std::string fMessage; +}; + +typedef std::vector<double> Vector; + +template<class type> +class SafePtr +{ +public: + SafePtr(type* _pType=0):pType(_pType){}; + virtual ~SafePtr(){delete pType;}; + SafePtr& operator=(type* _pType){delete pType; pType=_pType; return (*this);}; + bool isNull(){return (pType==0);}; + + operator type*(){return pType;}; + operator const type*() const {return pType;}; + + //operator type&(){ASSERT(pType);return *pType;}; + //operator const type&() const {ASSERT(pType);return *pType;}; + + type& operator*() {ASSERT(pType);return *pType;}; + const type& operator*() const {ASSERT(pType);return *pType;}; + + inline type* operator->() const + { + ASSERT(pType); + return pType; + } +private: + type* pType; +}; + +template<class type> +class AutoDeletePtrArray : public std::vector<type*> +{ +public: + virtual ~AutoDeletePtrArray() + { + std::remove_if(std::vector<type*>::begin(),std::vector<type*>::end(),deleteAll); + }; + static bool deleteAll( type * aElement ) { delete aElement; return true; } + + inline void setIndexShift(int aShift) + { + iMin = -aShift; + }; + + inline type& operator()(int aIndex){ + return *std::vector<type*>::at(aIndex - iMin); + } + + inline const type& operator()(int aIndex) const{ + return *std::vector<type*>::at(aIndex - iMin); + } + + inline type* operator[](int aIndex){ + return std::vector<type*>::at(aIndex - iMin); + } + + inline const type* operator[](int aIndex) const{ + return std::vector<type*>::at(aIndex - iMin); + } + + inline void add(type* aElem){ + std::vector<type*>::push_back(aElem); + } +private: + int iMin; +}; + + class ISmartReferencedObj +{ +public: + virtual void addRef()=0; + virtual void releaseRef()=0; +}; + +class SmartReferencedObj : public virtual ISmartReferencedObj{ +public: + SmartReferencedObj():iRefCount(0){}; + void addRef(){ + iRefCount++; + } + void releaseRef(){ + iRefCount--; + if (iRefCount<=0) { + delete this; + } + } + inline int RefCount() const{return iRefCount;}; + virtual ~SmartReferencedObj(){}; +private: + int iRefCount; +}; + +template <class I> +class TSmartReferencedObj : public I // It is assumed that I is interface which extends ISmartReferencedObj +{ +public: + TSmartReferencedObj():iRefCount(0){}; + void addRef(){ + iRefCount++; + } + void releaseRef(){ + iRefCount--; + if (iRefCount<=0) { + delete this; + } + } + inline int RefCount() const{return iRefCount;}; + virtual ~TSmartReferencedObj(){}; +private: + int iRefCount; +}; + +template <class T>//class T should have addRef() & releaseRef() methods +class SmartPtr +{ +public: + SmartPtr(T* pointee = 0) : iPointee(pointee){ + if (iPointee) iPointee->addRef(); + }; + + SmartPtr(const SmartPtr<T>& other) : iPointee(other.iPointee){ + if (iPointee) iPointee->addRef(); + }; + + inline SmartPtr& operator=(T* pointee){ + if(iPointee==pointee) + return *this; + if (iPointee) iPointee->releaseRef(); + iPointee = pointee; + if (pointee) iPointee->addRef(); + return *this; + } + + inline SmartPtr& operator=(const SmartPtr<T>& other){ + if (iPointee) iPointee->releaseRef(); + iPointee = other.iPointee; + if (iPointee) iPointee->addRef(); + return *this; + } + + ~SmartPtr(){ + if (iPointee) iPointee->releaseRef(); + } + + inline bool operator==(T* pointee) const{ + return iPointee==pointee; + } + + inline T& operator*() const + { + return *iPointee; + } + + inline T* operator->() const + { + return iPointee; + } + + inline operator T*() const + { + return iPointee; + } + + inline T& operator[](int) const{ + //! although operator T* is defined, smart pointers should not be used as C-arrays + NOT_IMPLEMENTED; + return *iPointee; + } + +private: + T* iPointee; +}; + +}//end of namespace Utils + +#endif /* UTILS_H */ + diff --git a/src/lib/main.cpp b/src/lib/main.cpp new file mode 100644 index 0000000..666d48c --- /dev/null +++ b/src/lib/main.cpp @@ -0,0 +1,63 @@ +/* + * main.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#include <cstdlib> +#include <time.h> +#include "Test.h" + +using namespace Utils; + +/* + * user_main function performing user defined tasks + * the function should be provided by end user + */ +extern int user_main(int argc, char** argv); + +int main(int argc, char** argv) { + int result = 0; + time_t startTime = time(0); + try + { + if(argc>=2 && (strcmp(argv[1],"--test"))==0) { + Test::TestList testList; + result = testList.Main(argc - 2, argv + 2); + } + else + result = user_main(argc, argv); + } + catch(Exception* ex) + { + std::cerr << ex->Message() << std::endl; + result = 1; + } + if(!result){ + std::cerr << "# calculation took " << time(0)-startTime << " sec" << std::endl; + } + return result; +} + diff --git a/src/lib/sophia2cpp.f b/src/lib/sophia2cpp.f new file mode 100644 index 0000000..1238450 --- /dev/null +++ b/src/lib/sophia2cpp.f @@ -0,0 +1,85 @@ + subroutine sample_photopion(L0,E0,eps,theta,iSec,energies,nTypes) + + IMPLICIT DOUBLE PRECISION (A-H,O-Z) + IMPLICIT INTEGER (I-N) + SAVE + DIMENSION energies(2000) + DIMENSION nTypes(2000) + COMMON /S_PLIST/ P(2000,5), LLIST(2000), NP, Ideb + + call eventgen(L0,E0,eps,theta,Imode) + iSec=NP + do i=1,NP + nTypes(i) = abs(LLIST(i)) + energies(i) = abs(P(i,4)) + enddo + + RETURN + END + + subroutine crossec(L0,eps_prime,sig) + + IMPLICIT DOUBLE PRECISION (A-H,O-Z) + IMPLICIT INTEGER (I-N) + SAVE + + sig = crossection(eps_prime,3,L0) + + RETURN + END + + subroutine sample_photopion_rel(L0,eps_prime,iSec,relEnergies,nTypes) + + IMPLICIT DOUBLE PRECISION (A-H,O-Z) + IMPLICIT INTEGER (I-N) + SAVE + DIMENSION relEnergies(2000) + DIMENSION nTypes(2000) + COMMON /S_PLIST/ P(2000,5), LLIST(2000), NP, Ideb + COMMON /S_MASS1/ AM(49), AM2(49) + +c Using proton rest frame + E0=AM(L0) +c we assume that lab frame speed is directed along z-axes +c than with good accuracy initial photon momentum is directed +c against z-axis + theta=0 + + call eventgen(L0,E0,eps_prime,theta,Imode) + iSec=NP + do i=1,NP + nTypes(i) = abs(LLIST(i)) +c relEnergies(i) - energy of final particle in units of +c initial nucleon energy in lab frame. Here we use the fact that nucleon +c rest frame speed in lab frame is directed along z-axis (against photon +c momentum in rest frame) with good accuracy and assume +c ultrarelativistic case: beta=1 and so the fraction doesn't depend on +c the initial nucleon gamma factor. +c The precise expression would be +c relEnergies(i) = abs(P(i,4)+beta*P(i,3))/AM(L0) +c where beta is speed of initial nucleon in the rest frame + relEnergies(i) = abs(P(i,4)+P(i,3))/AM(L0) + enddo + + RETURN + END + + subroutine get_mass(L0,pm) + + IMPLICIT DOUBLE PRECISION (A-H,O-Z) + IMPLICIT INTEGER (I-N) + SAVE + COMMON /S_MASS1/ AM(49), AM2(49) + pm = AM(L0) + RETURN + END + + subroutine set_random_seed(iseed) + + IMPLICIT DOUBLE PRECISION (A-H,O-Z) + IMPLICIT INTEGER (I-N) + COMMON/LUDATR/MRLU(6),RRLU(100) + SAVE + MRLU(1) = iseed + RETURN + END \ No newline at end of file diff --git a/src/lib/z2t.cpp b/src/lib/z2t.cpp new file mode 100644 index 0000000..2150e85 --- /dev/null +++ b/src/lib/z2t.cpp @@ -0,0 +1,55 @@ +/* + * z2t.cpp + * + * Author: + * Oleg Kalashev + * + * Copyright (c) 2020 Institute for Nuclear Research, RAS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "Cosmology.h" +#include "Units.h" +#include <iostream> +using namespace mcray; + +int main(int argc, char** argv) { + //Test::PrecisionTests::SetPrecision(128); + int result = 0; + try + { + double z=-1.; + if(argc<2 || sscanf(argv[1],"%lg",&z)!=1 || z<0) { + std::cerr << "Calculate light travel time (in Mpc) from remote object\n" + << "usage: " << argv[0] << "<z>" << std::endl; + return 1; + } + if(!cosmology.IsInitialized()) + cosmology.Init(z+10.); + double t = (cosmology.getAgeOfUniverse()-cosmology.z2t(z)); + std::cout << t/units.Mpc << std::endl; + } + catch(Exception* ex) + { + std::cerr << ex->Message() << std::endl; + result = 1; + } + return result; +} \ No newline at end of file -- GitLab