Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Candidate Splitting module #442

Merged
merged 5 commits into from
Oct 16, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,7 @@ add_library(crpropa SHARED
src/module/Acceleration.cpp
src/module/Boundary.cpp
src/module/BreakCondition.cpp
src/module/CandidateSplitting.cpp
src/module/DiffusionSDE.cpp
src/module/EMCascade.cpp
src/module/EMDoublePairProduction.cpp
Expand Down Expand Up @@ -656,6 +657,10 @@ if(ENABLE_TESTING)
target_link_libraries(testAdiabaticCooling crpropa gtest gtest_main pthread ${COVERAGE_LIBS})
add_test(testAdiabaticCooling testAdiabaticCooling)

add_executable(testCandidateSplitting test/testCandidateSplitting.cpp)
target_link_libraries(testCandidateSplitting crpropa gtest gtest_main pthread ${COVERAGE_LIBS})
add_test(testCandidateSplitting testCandidateSplitting)

sophieaerdker marked this conversation as resolved.
Show resolved Hide resolved

if(WITH_GALACTIC_LENSES)
add_executable(testGalacticMagneticLens test/testMagneticLens.cpp)
Expand Down
1 change: 1 addition & 0 deletions include/CRPropa.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "crpropa/module/Acceleration.h"
#include "crpropa/module/Boundary.h"
#include "crpropa/module/BreakCondition.h"
#include "crpropa/module/CandidateSplitting.h"
#include "crpropa/module/DiffusionSDE.h"
#include "crpropa/module/EMCascade.h"
#include "crpropa/module/EMDoublePairProduction.h"
Expand Down
80 changes: 80 additions & 0 deletions include/crpropa/module/CandidateSplitting.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#ifndef CRPROPA_CANDIDATEPLITTING_H
#define CRPROPA_CANDIDATEPLITTING_H

#include <string>
#include <iostream>
#include <cmath>
#include <cstdlib>
#include <sstream>

#include "crpropa/Vector3.h"
#include "crpropa/Module.h"
#include "crpropa/Units.h"
#include "kiss/logger.h"


namespace crpropa {

/**
@class CandidateSplitting
@brief Candidates are split into n copies when they cross specified energy bins. Weights are set accordingly.
sophieaerdker marked this conversation as resolved.
Show resolved Hide resolved
In case of Diffusice Shock Acceleration, splitting number can be adapted to expected spectral index to
compensate for the loss of particles per magnitude in energy
*/

sophieaerdker marked this conversation as resolved.
Show resolved Hide resolved
class CandidateSplitting: public Module {
private:
double n_split;
sophieaerdker marked this conversation as resolved.
Show resolved Hide resolved
double minWeight;
std::vector<double> Ebins;

public:

CandidateSplitting();

CandidateSplitting(int n_split, double Emin, double Emax, double n_bins, double minWeight);
sophieaerdker marked this conversation as resolved.
Show resolved Hide resolved
/** Constructor
sophieaerdker marked this conversation as resolved.
Show resolved Hide resolved
@param n_split Number of copies candidates are split
@param Emin Minimal energy for splitting
@param Emax Maximal energy for splitting
@param minWeight Mimimal Weight
@param n_bins Number of energy bins
*/
CandidateSplitting(int n_split, double Emin, double Emax, double n_bins, double minWeight, bool log);
sophieaerdker marked this conversation as resolved.
Show resolved Hide resolved
/** Constructor
@param n_split Number of copies candidates are split
@param Emin Minimal energy for splitting
@param Emax Maximal energy for splitting
@param n_bins Number of energy bins
@param minWeight Mimimal Weight
@param log Energy bins in log
*/

CandidateSplitting(double SpectralIndex, double Emin, int nBins);
/** Constructor
@param SpectralIndex Absolute value of expected spectral index determines splitting number
@param Emin Minimal energy for splitting
@param nBins Number of bins in energy, with dE(spectralIndex) it determines Emax
*/

void process(Candidate *c) const;

void setEnergyBins(double Emin, double Emax, double n_bins, bool log);

void setEnergyBinsDSA(double Emin, double dE, int n);

void setNsplit(int n);

void setMinimalWeight(double w);

int getNsplit() const;

double getMinimalWeight() const;

const std::vector<double>& getEnergyBins() const;

};
/** @}*/
sophieaerdker marked this conversation as resolved.
Show resolved Hide resolved

} // namespace crpropa
#endif // CRPROPA_PARTICLESPLITTING_H
sophieaerdker marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions python/2_headers.i
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,7 @@ using namespace crpropa; // for usage of namespace in header files, necessary
%include "crpropa/module/EMInverseComptonScattering.h"
%include "crpropa/module/SynchrotronRadiation.h"
%include "crpropa/module/AdiabaticCooling.h"
%include "crpropa/module/CandidateSplitting.h"

%template(IntSet) std::set<int>;
%include "crpropa/module/Tools.h"
Expand Down
154 changes: 154 additions & 0 deletions src/module/CandidateSplitting.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
#include "crpropa/module/CandidateSplitting.h"

namespace crpropa {

CandidateSplitting::CandidateSplitting() {
// no particle splitting if EnergyBins and NSplit are not specified
setNsplit(0);
setMinimalWeight(1.);
}

CandidateSplitting::CandidateSplitting(int n_split, double Emin, double Emax, double n_bins, double minWeight) {
// linear energy bins:
setNsplit(n_split);
setEnergyBins(Emin, Emax, n_bins, false);
setMinimalWeight(minWeight);
}

CandidateSplitting::CandidateSplitting(int n_split, double Emin, double Emax, double n_bins, double minWeight, bool log) {
setNsplit(n_split);
setEnergyBins(Emin, Emax, n_bins, log);
setMinimalWeight(minWeight);
}

CandidateSplitting::CandidateSplitting(double SpectralIndex, double Emin, int nBins) {
// to use with Diffusive Shock Acceleration

sophieaerdker marked this conversation as resolved.
Show resolved Hide resolved
if (SpectralIndex <= 0){
sophieaerdker marked this conversation as resolved.
Show resolved Hide resolved
throw std::runtime_error(
"CandidateSplitting: spectralIndex <= 0 !");
}

setNsplit(2); // always split in 2, calculate bins in energy for given spectrum:
double dE = pow(1./2, 1./(-SpectralIndex + 1));
sophieaerdker marked this conversation as resolved.
Show resolved Hide resolved
setEnergyBinsDSA(Emin, dE, nBins);
setMinimalWeight(1./pow(2, nBins));

}

sophieaerdker marked this conversation as resolved.
Show resolved Hide resolved

void CandidateSplitting::process(Candidate *c) const {

sophieaerdker marked this conversation as resolved.
Show resolved Hide resolved
double currE = c->current.getEnergy();
double prevE = c->previous.getEnergy();

if (c->getWeight() <= minWeight){
// minimal weight reached, no splitting
return;
}
if (currE < Ebins[0] || n_split == 0 ){
// current energy is smaller than first bin -> no splitting
// or, number of splits = 0
return;
}
for (size_t i = 0; i < Ebins.size(); ++i){

if( prevE < Ebins[i] ){
// previous energy is in energy bin [i-1, i]
if(currE < Ebins[i]){
//assuming that dE greater than 0, prevE and E in same energy bin -> no splitting
return;
}
// current energy is in energy bin [i,i+1] or higher -> particle splitting for each crossing
for (size_t j = i; j < Ebins.size(); ++j ){

// adapted from Acceleration Module:
c->updateWeight(1. / n_split); // * 1/n_split

for (int i = 1; i < n_split; i++) {

ref_ptr<Candidate> new_candidate = c->clone(false);
new_candidate->parent = c;
uint64_t snr = Candidate::getNextSerialNumber();
new_candidate->setSerialNumber(snr);
new_candidate->previous.setEnergy(currE); // so that new candidate is not split again in next step!
//InteractionTag is PRIM, physically no new particles are created
c->addSecondary(new_candidate);
Candidate::setNextSerialNumber(snr + 1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this save for parallelization? I would expect a #pragma omp critical. Maybe the creation of new candidates should be compared.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently, it works without setting the new serial number... So, I removed that part from the code and added the update of the serial number to the test

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And we should remove that from Acceleration as well :-)

}


sophieaerdker marked this conversation as resolved.
Show resolved Hide resolved
if (j < Ebins.size()-1 && currE < Ebins[j+1]){
// candidate is in energy bin [j, j+1] -> no further splitting
return;
}
}

return;

sophieaerdker marked this conversation as resolved.
Show resolved Hide resolved
}
}
}


void CandidateSplitting::setEnergyBins(double Emin, double Emax, double n_bins, bool log) {

sophieaerdker marked this conversation as resolved.
Show resolved Hide resolved
Ebins.resize(0);

if (Emin > Emax){
throw std::runtime_error(
"CandidateSplitting: Emin > Emax!");
}

double dE = (Emax-Emin)/n_bins;

for (size_t i = 0; i < n_bins; ++i) {
if (log == true) {
Ebins.push_back(Emin * pow(Emax / Emin, i / (n_bins - 1.0)));
} else {
Ebins.push_back(Emin + i * dE);
}
}

sophieaerdker marked this conversation as resolved.
Show resolved Hide resolved
}


sophieaerdker marked this conversation as resolved.
Show resolved Hide resolved
void CandidateSplitting::setEnergyBinsDSA(double Emin, double dE, int n) {

sophieaerdker marked this conversation as resolved.
Show resolved Hide resolved
Ebins.resize(0);

for (size_t i = 1; i < n + 1; ++i) {

Ebins.push_back(Emin * pow(dE, i));
}

}

sophieaerdker marked this conversation as resolved.
Show resolved Hide resolved

const std::vector<double>& CandidateSplitting::getEnergyBins() const {
return Ebins;
}

void CandidateSplitting::setNsplit(int n) {

sophieaerdker marked this conversation as resolved.
Show resolved Hide resolved
n_split = n;
}

void CandidateSplitting::setMinimalWeight(double w) {

sophieaerdker marked this conversation as resolved.
Show resolved Hide resolved
minWeight = w;
}

int CandidateSplitting::getNsplit() const {

return n_split;
}

double CandidateSplitting::getMinimalWeight() const {

return minWeight;
}


} // end namespace crpropa

90 changes: 90 additions & 0 deletions test/testCandidateSplitting.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#include "crpropa/Units.h"
#include "crpropa/Common.h"
#include "crpropa/ParticleID.h"
#include "crpropa/module/CandidateSplitting.h"

#include "gtest/gtest.h"
#include <stdexcept>
#include <cmath>

namespace crpropa {

TEST(testCandidateSplitting, SimpleTest) {

sophieaerdker marked this conversation as resolved.
Show resolved Hide resolved

int n_split = 2;
int n_bins = 4;
double minWeight = pow(1./n_split, 2);
double Emin = 1*GeV;
sophieaerdker marked this conversation as resolved.
Show resolved Hide resolved
double Emax = 10*GeV;

CandidateSplitting split_lin(n_split, Emin, Emax, n_bins, minWeight);
sophieaerdker marked this conversation as resolved.
Show resolved Hide resolved
double dE = (Emax-Emin)/n_bins;
sophieaerdker marked this conversation as resolved.
Show resolved Hide resolved
EXPECT_DOUBLE_EQ(split_lin.getEnergyBins()[0], Emin);
EXPECT_DOUBLE_EQ(split_lin.getEnergyBins()[1], Emin+dE);

EXPECT_EQ(split_lin.getNsplit(), n_split);
EXPECT_DOUBLE_EQ(split_lin.getMinimalWeight(), minWeight);

CandidateSplitting split_log(n_split, Emin, Emax, n_bins, minWeight, true);
double dE_log = pow(Emax / Emin, 1./(n_bins - 1.0));
EXPECT_DOUBLE_EQ(split_log.getEnergyBins()[0], Emin);
EXPECT_DOUBLE_EQ(split_log.getEnergyBins()[1], Emin*dE_log);

double spectralIndex = 2.;
CandidateSplitting split_dsa(spectralIndex, Emin, n_bins);
double dE_dsa = pow(1./2, 1./(-spectralIndex + 1));
EXPECT_DOUBLE_EQ(split_dsa.getEnergyBins()[0], Emin*dE_dsa);
EXPECT_DOUBLE_EQ(split_dsa.getEnergyBins()[n_bins-1], Emin * pow(dE_dsa, n_bins));

sophieaerdker marked this conversation as resolved.
Show resolved Hide resolved

}


TEST(testCandidateSplitting, CheckSplits) {

sophieaerdker marked this conversation as resolved.
Show resolved Hide resolved
int n_split = 2;
int n_bins = 3;
double Emin = 1*GeV;
sophieaerdker marked this conversation as resolved.
Show resolved Hide resolved
double Emax = 10*GeV;
double minWeight = pow(1./n_split, 4);

CandidateSplitting splitting(n_split, Emin, Emax, n_bins, minWeight);
Candidate c(nucleusId(1,1),0.5*GeV);
double weight = 1.0;

splitting.process(&c); // no split
EXPECT_DOUBLE_EQ(c.getWeight(), weight);

c.current.setEnergy(2*GeV);
splitting.process(&c); // 1. split
weight = weight/n_split;
EXPECT_DOUBLE_EQ(c.getWeight(), weight);
c.previous.setEnergy(2*GeV);

c.current.setEnergy(6*GeV);
splitting.process(&c); // 2. split
weight = weight/n_split;
EXPECT_DOUBLE_EQ(c.getWeight(), weight);
c.previous.setEnergy(6*GeV);

c.current.setEnergy(0.5*GeV);
splitting.process(&c); // no split, cooling
EXPECT_DOUBLE_EQ(c.getWeight(), weight);
c.previous.setEnergy(0.5*GeV);

c.current.setEnergy(6*GeV);
splitting.process(&c); // 3. & 4. split, crosses two boundaries
weight = weight/n_split/n_split;
EXPECT_DOUBLE_EQ(c.getWeight(), weight);
c.previous.setEnergy(6*GeV);

c.current.setEnergy(8*GeV);
splitting.process(&c); // no split, minimal weight reached
EXPECT_DOUBLE_EQ(c.getWeight(), weight);
c.previous.setEnergy(8*GeV);

}


} //namespace crpropa
Loading