Program Listing for File DiffusionSDE.cpp
↰ Return to documentation for file (src/module/DiffusionSDE.cpp
)
#include "crpropa/module/DiffusionSDE.h"
using namespace crpropa;
// Defining Cash-Karp coefficients
const double a[] = { 0., 0., 0., 0., 0., 0., 1. / 5., 0., 0., 0., 0.,
0., 3. / 40., 9. / 40., 0., 0., 0., 0., 3. / 10., -9. / 10., 6. / 5.,
0., 0., 0., -11. / 54., 5. / 2., -70. / 27., 35. / 27., 0., 0., 1631.
/ 55296., 175. / 512., 575. / 13824., 44275. / 110592., 253.
/ 4096., 0. };
const double b[] = { 37. / 378., 0, 250. / 621., 125. / 594., 0., 512.
/ 1771. };
const double bs[] = { 2825. / 27648., 0., 18575. / 48384., 13525.
/ 55296., 277. / 14336., 1. / 4. };
DiffusionSDE::DiffusionSDE(ref_ptr<MagneticField> magneticField, double tolerance,
double minStep, double maxStep, double epsilon) :
minStep(0)
{
setMagneticField(magneticField);
setMaximumStep(maxStep);
setMinimumStep(minStep);
setTolerance(tolerance);
setEpsilon(epsilon);
setScale(1.);
setAlpha(1./3.);
}
DiffusionSDE::DiffusionSDE(ref_ptr<MagneticField> magneticField, ref_ptr<AdvectionField> advectionField, double tolerance, double minStep, double maxStep, double epsilon) :
minStep(0)
{
setMagneticField(magneticField);
setAdvectionField(advectionField);
setMaximumStep(maxStep);
setMinimumStep(minStep);
setTolerance(tolerance);
setEpsilon(epsilon);
setScale(1.);
setAlpha(1./3.);
}
void DiffusionSDE::process(Candidate *candidate) const {
// save the new previous particle state
ParticleState ¤t = candidate->current;
candidate->previous = current;
double h = clip(candidate->getNextStep(), minStep, maxStep) / c_light;
Vector3d PosIn = current.getPosition();
Vector3d DirIn = current.getDirection();
// rectilinear propagation for neutral particles
// If an advection field is provided the drift is also included
if (current.getCharge() == 0) {
Vector3d dir = current.getDirection();
Vector3d Pos = current.getPosition();
Vector3d LinProp(0.);
if (advectionField){
driftStep(Pos, LinProp, h);
}
current.setPosition(Pos + LinProp + dir*h*c_light);
candidate->setCurrentStep(h * c_light);
candidate->setNextStep(maxStep);
return;
}
double z = candidate->getRedshift();
double rig = current.getEnergy() / current.getCharge();
// Calculate the Diffusion tensor
double BTensor[] = {0., 0., 0., 0., 0., 0., 0., 0., 0.};
calculateBTensor(rig, BTensor, PosIn, DirIn, z);
// Generate random numbers
double eta[] = {0., 0., 0.};
for(size_t i=0; i < 3; i++) {
eta[i] = Random::instance().randNorm();
}
double TStep = BTensor[0] * eta[0];
double NStep = BTensor[4] * eta[1];
double BStep = BTensor[8] * eta[2];
Vector3d TVec(0.);
Vector3d NVec(0.);
Vector3d BVec(0.);
Vector3d DirOut = Vector3d(0.);
double propTime = TStep * sqrt(h) / c_light;
size_t counter = 0;
double r=42.; //arbitrary number larger than one
do {
Vector3d PosOut = Vector3d(0.);
Vector3d PosErr = Vector3d(0.);
tryStep(PosIn, PosOut, PosErr, z, propTime);
// calculate the relative position error r and the next time step h
r = PosErr.getR() / tolerance;
propTime *= 0.5;
counter += 1;
// Check for better break condition
} while (r > 1 && fabs(propTime) >= minStep/c_light);
size_t stepNumber = pow(2, counter-1);
double allowedTime = TStep * sqrt(h) / c_light / stepNumber;
Vector3d Start = PosIn;
Vector3d PosOut = Vector3d(0.);
Vector3d PosErr = Vector3d(0.);
for (size_t j=0; j<stepNumber; j++) {
tryStep(Start, PosOut, PosErr, z, allowedTime);
Start = PosOut;
}
// Normalize the tangent vector
TVec = (PosOut-PosIn).getUnitVector();
// Exception: If the magnetic field vanishes: Use only advection.
// If an advection field is not provided --> rectilinear propagation.
double tTest = TVec.getR();
if (tTest != tTest) {
Vector3d dir = current.getDirection();
Vector3d Pos = current.getPosition();
Vector3d LinProp(0.);
if (advectionField){
driftStep(Pos, LinProp, h);
current.setPosition(Pos + LinProp);
candidate->setCurrentStep(h*c_light);
double newStep = 5*h*c_light;
newStep = clip(newStep, minStep, maxStep);
candidate->setNextStep(newStep);
return;
}
current.setPosition(Pos + dir*h*c_light);
candidate->setCurrentStep(h*c_light);
double newStep = 5*h*c_light;
newStep = clip(newStep, minStep, maxStep);
candidate->setNextStep(newStep);
return;
}
// Choose a random perpendicular vector as the Normal-vector.
// Prevent 'nan's in the NVec-vector in the case of <TVec, NVec> = 0.
while (NVec.getR()==0.){
Vector3d RandomVector = Random::instance().randVector();
NVec = TVec.cross( RandomVector );
}
NVec = NVec.getUnitVector();
// Calculate the Binormal-vector
BVec = (TVec.cross(NVec)).getUnitVector();
// Calculate the advection step
Vector3d LinProp(0.);
if (advectionField){
driftStep(PosIn, LinProp, h);
}
// Integration of the SDE with a Mayorama-Euler-method
Vector3d PO = PosOut + LinProp + (NVec * NStep + BVec * BStep) * sqrt(h) ;
// Throw error message if something went wrong with propagation.
// Deactivate candidate.
bool NaN = std::isnan(PO.getR());
if (NaN == true){
candidate->setActive(false);
KISS_LOG_WARNING
<< "\nCandidate with 'nan'-position occured: \n"
<< "position = " << PO << "\n"
<< "PosIn = " << PosIn << "\n"
<< "TVec = " << TVec << "\n"
<< "TStep = " << std::abs(TStep) << "\n"
<< "NVec = " << NVec << "\n"
<< "NStep = " << NStep << "\n"
<< "BVec = " << BVec << "\n"
<< "BStep = " << BStep << "\n"
<< "Candidate is deactivated!\n";
return;
}
//DirOut = (PO - PosIn - LinProp).getUnitVector(); //Advection does not change the momentum vector
// Random direction around the tangential direction accounts for the pitch angle average.
DirOut = Random::instance().randConeVector(TVec, M_PI/2.);
current.setPosition(PO);
current.setDirection(DirOut);
candidate->setCurrentStep(h * c_light);
double nextStep;
if (stepNumber>1){
nextStep = h*pow(stepNumber, -2.)*c_light;
}
else {
nextStep = 4 * h*c_light;
}
candidate->setNextStep(nextStep);
// Debugging and Testing
// Delete comments if additional information should be stored in candidate
// This property "arcLength" can be interpreted as the effective arclength
// of the propagation along a magnetic field line.
/*
const std::string AL = "arcLength";
if (candidate->hasProperty(AL) == false){
double arcLen = (TStep + NStep + BStep) * sqrt(h);
candidate->setProperty(AL, arcLen);
return;
}
else {
double arcLen = candidate->getProperty(AL);
arcLen += (TStep + NStep + BStep) * sqrt(h);
candidate->setProperty(AL, arcLen);
}
*/
}
void DiffusionSDE::tryStep(const Vector3d &PosIn, Vector3d &POut, Vector3d &PosErr,double z, double propStep) const {
Vector3d k[] = {Vector3d(0.),Vector3d(0.),Vector3d(0.),Vector3d(0.),Vector3d(0.),Vector3d(0.)};
POut = PosIn;
//calculate the sum k_i * b_i
for (size_t i = 0; i < 6; i++) {
Vector3d y_n = PosIn;
for (size_t j = 0; j < i; j++)
y_n += k[j] * a[i * 6 + j] * propStep;
// update k_i = direction of the regular magnetic mean field
Vector3d BField = getMagneticFieldAtPosition(y_n, z);
k[i] = BField.getUnitVector() * c_light;
POut += k[i] * b[i] * propStep;
PosErr += (k[i] * (b[i] - bs[i])) * propStep / kpc;
}
}
void DiffusionSDE::driftStep(const Vector3d &pos, Vector3d &linProp, double h) const {
Vector3d advField = getAdvectionFieldAtPosition(pos);
linProp += advField * h;
return;
}
void DiffusionSDE::calculateBTensor(double r, double BTen[], Vector3d pos, Vector3d dir, double z) const {
double DifCoeff = scale * 6.1e24 * pow((std::abs(r) / 4.0e9), alpha);
BTen[0] = pow( 2 * DifCoeff, 0.5);
BTen[4] = pow(2 * epsilon * DifCoeff, 0.5);
BTen[8] = pow(2 * epsilon * DifCoeff, 0.5);
return;
}
void DiffusionSDE::setMinimumStep(double min) {
if (min < 0)
throw std::runtime_error("DiffusionSDE: minStep < 0 ");
if (min > maxStep)
throw std::runtime_error("DiffusionSDE: minStep > maxStep");
minStep = min;
}
void DiffusionSDE::setMaximumStep(double max) {
if (max < minStep)
throw std::runtime_error("DiffusionSDE: maxStep < minStep");
maxStep = max;
}
void DiffusionSDE::setTolerance(double tol) {
if ((tol > 1) or (tol < 0))
throw std::runtime_error(
"DiffusionSDE: tolerance error not in range 0-1");
tolerance = tol;
}
void DiffusionSDE::setEpsilon(double e) {
if ((e > 1) or (e < 0))
throw std::runtime_error(
"DiffusionSDE: epsilon not in range 0-1");
epsilon = e;
}
void DiffusionSDE::setAlpha(double a) {
if ((a > 2.) or (a < 0))
throw std::runtime_error(
"DiffusionSDE: alpha not in range 0-2");
alpha = a;
}
void DiffusionSDE::setScale(double s) {
if (s < 0)
throw std::runtime_error(
"DiffusionSDE: Scale error: Scale < 0");
scale = s;
}
void DiffusionSDE::setMagneticField(ref_ptr<MagneticField> f) {
magneticField = f;
}
void DiffusionSDE::setAdvectionField(ref_ptr<AdvectionField> f) {
advectionField = f;
}
double DiffusionSDE::getMinimumStep() const {
return minStep;
}
double DiffusionSDE::getMaximumStep() const {
return maxStep;
}
double DiffusionSDE::getTolerance() const {
return tolerance;
}
double DiffusionSDE::getEpsilon() const {
return epsilon;
}
double DiffusionSDE::getAlpha() const {
return alpha;
}
double DiffusionSDE::getScale() const {
return scale;
}
ref_ptr<MagneticField> DiffusionSDE::getMagneticField() const {
return magneticField;
}
Vector3d DiffusionSDE::getMagneticFieldAtPosition(Vector3d pos, double z) const {
Vector3d B(0, 0, 0);
try {
// check if field is valid and use the field vector at the
// position pos with the redshift z
if (magneticField.valid())
B = magneticField->getField(pos, z);
}
catch (std::exception &e) {
KISS_LOG_ERROR << "DiffusionSDE: Exception in DiffusionSDE::getMagneticFieldAtPosition.\n"
<< e.what();
}
return B;
}
ref_ptr<AdvectionField> DiffusionSDE::getAdvectionField() const {
return advectionField;
}
Vector3d DiffusionSDE::getAdvectionFieldAtPosition(Vector3d pos) const {
Vector3d AdvField(0.);
try {
// check if field is valid and use the field vector at the
// position pos
if (advectionField.valid())
AdvField = advectionField->getField(pos);
}
catch (std::exception &e) {
KISS_LOG_ERROR << "DiffusionSDE: Exception in DiffusionSDE::getAdvectionFieldAtPosition.\n"
<< e.what();
}
return AdvField;
}
std::string DiffusionSDE::getDescription() const {
std::stringstream s;
s << "minStep: " << minStep / kpc << " kpc, ";
s << "maxStep: " << maxStep / kpc << " kpc, ";
s << "tolerance: " << tolerance << "\n";
if (epsilon != 0.1) {
s << "epsilon: " << epsilon << ", ";
}
if (alpha != 1./3.) {
s << "alpha: " << alpha << "\n";
}
if (scale != 1.) {
s << "D_0: " << scale*6.1e24 << " m^2/s" << "\n";
}
return s.str();
}