LCOV - code coverage report
Current view: top level - src/module - PropagationBP.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 92 104 88.5 %
Date: 2024-04-29 14:43:01 Functions: 15 16 93.8 %

          Line data    Source code
       1             : #include "crpropa/module/PropagationBP.h"
       2             : 
       3             : #include <sstream>
       4             : #include <stdexcept>
       5             : #include <vector>
       6             : 
       7             : namespace crpropa {
       8          28 :         void PropagationBP::tryStep(const Y &y, Y &out, Y &error, double h,
       9             :                         ParticleState &particle, double z, double q, double m) const {
      10          28 :                 out = dY(y.x, y.u, h, z, q, m);  // 1 step with h
      11             : 
      12          28 :                 Y outHelp = dY(y.x, y.u, h/2, z, q, m);  // 2 steps with h/2
      13          28 :                 Y outCompare = dY(outHelp.x, outHelp.u, h/2, z, q, m);
      14             : 
      15          28 :                 error = errorEstimation(out.x , outCompare.x , h);
      16          28 :         }
      17             : 
      18             : 
      19         103 :         PropagationBP::Y PropagationBP::dY(Vector3d pos, Vector3d dir, double step,
      20             :                         double z, double q, double m) const {
      21             :                 // half leap frog step in the position
      22             :                 pos += dir * step / 2.;
      23             : 
      24             :                 // get B field at particle position
      25         103 :                 Vector3d B = getFieldAtPosition(pos, z);
      26             : 
      27             :                 // Boris help vectors
      28             :                 Vector3d t = B * q / 2 / m * step / c_light;
      29         103 :                 Vector3d s = t * 2 / (1 + t.dot(t));
      30             :                 Vector3d v_help;
      31             : 
      32             :                 // Boris push
      33             :                 v_help = dir + dir.cross(t);
      34             :                 dir = dir + v_help.cross(s);
      35             : 
      36             :                 // the other half leap frog step in the position
      37             :                 pos += dir * step / 2.;
      38         103 :                 return Y(pos, dir);
      39             :         }
      40             : 
      41             : 
      42             :         // with a fixed step size
      43          10 :         PropagationBP::PropagationBP(ref_ptr<MagneticField> field, double fixedStep) :
      44          10 :                         minStep(0) {
      45          10 :                 setField(field);
      46          10 :                 setTolerance(0.42);
      47          10 :                 setMaximumStep(fixedStep);
      48          10 :                 setMinimumStep(fixedStep);
      49          10 :         }
      50             : 
      51             : 
      52             :         // with adaptive step size
      53           5 :         PropagationBP::PropagationBP(ref_ptr<MagneticField> field, double tolerance, double minStep, double maxStep) :
      54           5 :                         minStep(0) {
      55           7 :                 setField(field);
      56           5 :                 setTolerance(tolerance);
      57           4 :                 setMaximumStep(maxStep);
      58           4 :                 setMinimumStep(minStep);
      59           3 :         }
      60             : 
      61             : 
      62          37 :         void PropagationBP::process(Candidate *candidate) const {
      63             :                 // save the new previous particle state
      64          37 :                 ParticleState &current = candidate->current;
      65             :                 candidate->previous = current;
      66             : 
      67          37 :                 Y yIn(current.getPosition(), current.getDirection());
      68             : 
      69             :                 // calculate charge of particle
      70          37 :                 double q = current.getCharge();
      71          37 :                 double step = maxStep;
      72             : 
      73             :                 // rectilinear propagation for neutral particles
      74          37 :                 if (q == 0) {
      75           2 :                         step = clip(candidate->getNextStep(), minStep, maxStep);
      76           1 :                         current.setPosition(yIn.x + yIn.u * step);
      77           1 :                         candidate->setCurrentStep(step);
      78           1 :                         candidate->setNextStep(maxStep);
      79             :                         return;
      80             :                 }
      81             : 
      82             :                 Y yOut, yErr;
      83          36 :                 double newStep = step;
      84          36 :                 double z = candidate->getRedshift();
      85          36 :                 double m = current.getEnergy()/(c_light * c_light);
      86             : 
      87             :                 // if minStep is the same as maxStep the adaptive algorithm with its error
      88             :                 // estimation is not needed and the computation time can be saved:
      89          36 :                 if (minStep == maxStep){
      90          19 :                         yOut = dY(yIn.x, yIn.u, step, z, q, m);
      91             :                 } else {
      92          17 :                         step = clip(candidate->getNextStep(), minStep, maxStep);
      93          17 :                         newStep = step;
      94             :                         double r = 42;  // arbitrary value
      95             : 
      96             :                         // try performing step until the target error (tolerance) or the minimum/maximum step size has been reached
      97             :                         while (true) {
      98          28 :                                 tryStep(yIn, yOut, yErr, step, current, z, q, m);
      99          28 :                                 r = yErr.u.getR() / tolerance;  // ratio of absolute direction error and tolerance
     100          28 :                                 if (r > 1) {  // large direction error relative to tolerance, try to decrease step size
     101          18 :                                         if (step == minStep)  // already minimum step size
     102             :                                                 break;
     103             :                                         else {
     104          11 :                                                 newStep = step * 0.95 * pow(r, -0.2);
     105          19 :                                                 newStep = std::max(newStep, 0.1 * step); // limit step size decrease
     106          11 :                                                 newStep = std::max(newStep, minStep); // limit step size to minStep
     107             :                                                 step = newStep;
     108             :                                         }
     109             :                                 } else {  // small direction error relative to tolerance, try to increase step size
     110          10 :                                         if (step != maxStep) {  // only update once if maximum step size yet not reached
     111          10 :                                                 newStep = step * 0.95 * pow(r, -0.2);
     112          17 :                                                 newStep = std::min(newStep, 5 * step); // limit step size increase
     113          10 :                                                 newStep = std::min(newStep, maxStep); // limit step size to maxStep
     114             :                                         }
     115             :                                         break;
     116             :                                 }
     117             :                         }
     118             :                 }
     119             : 
     120          36 :                 current.setPosition(yOut.x);
     121          36 :                 current.setDirection(yOut.u.getUnitVector());
     122          36 :                 candidate->setCurrentStep(step);
     123          36 :                 candidate->setNextStep(newStep);
     124             :         }
     125             : 
     126             : 
     127          16 :         void PropagationBP::setField(ref_ptr<MagneticField> f) {
     128          16 :                 field = f;
     129          16 :         }
     130             : 
     131             : 
     132           2 :         ref_ptr<MagneticField> PropagationBP::getField() const {
     133           2 :                 return field;
     134             :         }
     135             : 
     136             : 
     137         104 :         Vector3d PropagationBP::getFieldAtPosition(Vector3d pos, double z) const {
     138             :                 Vector3d B(0, 0, 0);
     139             :                 try {
     140             :                         // check if field is valid and use the field vector at the
     141             :                         // position pos with the redshift z
     142         104 :                         if (field.valid())
     143         104 :                                 B = field->getField(pos, z);
     144           0 :                 } catch (std::exception &e) {
     145           0 :                         KISS_LOG_ERROR  << "PropagationBP: Exception in PropagationBP::getFieldAtPosition.\n"
     146           0 :                                         << e.what();
     147           0 :                 }       
     148         104 :                 return B;
     149             :         }
     150             : 
     151             : 
     152          28 :         double PropagationBP::errorEstimation(const Vector3d x1, const Vector3d x2, double step) const {
     153             :                 // compare the position after one step with the position after two steps with step/2.
     154             :                 Vector3d diff = (x1 - x2);
     155             : 
     156          28 :                 double S = diff.getR() / (step * (1 - 1/4.) );  // 1/4 = (1/2)²  number of steps for x1 divided by number of steps for x2 to the power of p (order)
     157             : 
     158          28 :                 return S;
     159             :         }
     160             : 
     161             : 
     162          22 :         void PropagationBP::setTolerance(double tol) {
     163          22 :                 if ((tol > 1) or (tol < 0))
     164           2 :                         throw std::runtime_error(
     165           4 :                                         "PropagationBP: target error not in range 0-1");
     166          20 :                 tolerance = tol;
     167          20 :         }
     168             : 
     169             : 
     170          24 :         void PropagationBP::setMinimumStep(double min) {
     171          24 :                 if (min < 0)
     172           1 :                         throw std::runtime_error("PropagationBP: minStep < 0 ");
     173          23 :                 if (min > maxStep)
     174           2 :                         throw std::runtime_error("PropagationBP: minStep > maxStep");
     175          21 :                 minStep = min;
     176          21 :         }
     177             : 
     178             : 
     179          22 :         void PropagationBP::setMaximumStep(double max) {
     180          22 :                 if (max < minStep)
     181           1 :                         throw std::runtime_error("PropagationBP: maxStep < minStep");
     182          21 :                 maxStep = max;
     183          21 :         }
     184             : 
     185             : 
     186           2 :         double PropagationBP::getTolerance() const {
     187           2 :                 return tolerance;
     188             :         }
     189             : 
     190             : 
     191           2 :         double PropagationBP::getMinimumStep() const {
     192           2 :                 return minStep;
     193             :         }
     194             : 
     195             : 
     196           5 :         double PropagationBP::getMaximumStep() const {
     197           5 :                 return maxStep;
     198             :         }
     199             : 
     200             : 
     201           0 :         std::string PropagationBP::getDescription() const {
     202           0 :                 std::stringstream s;
     203           0 :                 s << "Propagation in magnetic fields using the adaptive Boris push method.";
     204           0 :                 s << " Target error: " << tolerance;
     205           0 :                 s << ", Minimum Step: " << minStep / kpc << " kpc";
     206           0 :                 s << ", Maximum Step: " << maxStep / kpc << " kpc";
     207           0 :                 return s.str();
     208           0 :         }
     209             : } // namespace crpropa

Generated by: LCOV version 1.14