Program Listing for File Acceleration.cpp

Return to documentation for file (src/module/Acceleration.cpp)

#include "crpropa/module/Acceleration.h"
#include <crpropa/Common.h>
#include <crpropa/Random.h>
#include <cmath>

namespace crpropa {

AbstractAccelerationModule::AbstractAccelerationModule(double _stepLength)
        : crpropa::Module(), stepLength(_stepLength) {}


void AbstractAccelerationModule::add(StepLengthModifier *modifier) {
        modifiers.push_back(modifier);
}


void AbstractAccelerationModule::scatter(
        crpropa::Candidate *candidate,
        const crpropa::Vector3d &scatter_center_velocity) const {
        // particle momentum in lab frame
        const double E = candidate->current.getEnergy();
        const crpropa::Vector3d p = candidate->current.getMomentum();

        // transform to rest frame of scatter center (p: prime)
        const double beta = scatter_center_velocity.getR() / crpropa::c_light;
        const double gamma = 1. / sqrt(1 - beta * beta);
        const double Ep = gamma * (E - scatter_center_velocity.dot(p));
        const crpropa::Vector3d pp = (p - scatter_center_velocity* E /
                (crpropa::c_light * crpropa::c_light)) * gamma;

        // scatter into random direction
        const crpropa::Vector3d pp_new = crpropa::Random::instance().randVector() * pp.getR();

        // transform back
        const double E_new = gamma * (Ep + scatter_center_velocity.dot(pp_new));
        const crpropa::Vector3d p_new = (pp_new + scatter_center_velocity * Ep /
                (crpropa::c_light * crpropa::c_light)) * gamma;

        // update candidate properties
        candidate->current.setEnergy(E_new);
        candidate->current.setDirection(p_new / p_new.getR());
}


void AbstractAccelerationModule::process(crpropa::Candidate *candidate) const {
        double currentStepLength = stepLength;
        for (auto m : modifiers) {
                currentStepLength = m->modify(currentStepLength, candidate);
        }

        double step = candidate->getCurrentStep();
        while (step > 0) {
                double randDistance = -1. * log(crpropa::Random::instance().rand()) * currentStepLength;

                if (step < randDistance) {
                        candidate->limitNextStep(0.1 * currentStepLength);
                        return;
                }
                scatter(candidate, scatterCenterVelocity(candidate));
                step -= randDistance;
        }
}


SecondOrderFermi::SecondOrderFermi(double scatterVelocity, double stepLength,
                                                                   unsigned int sizeOfPitchangleTable)
        : AbstractAccelerationModule(stepLength),
          scatterVelocity(scatterVelocity) {
        setDescription("SecondOrderFermi Acceleration");
        angle.resize(sizeOfPitchangleTable);
        angleCDF.resize(sizeOfPitchangleTable);

        // have a discretized table of beamed pitch angles
        for (unsigned int i =0; i < sizeOfPitchangleTable; i++) {
                angle[i] = i * M_PI / (sizeOfPitchangleTable-1);
                angleCDF[i] = (angle[i] +scatterVelocity / crpropa::c_light * sin(angle[i])) / M_PI;
        }
}


crpropa::Vector3d SecondOrderFermi::scatterCenterVelocity(crpropa::Candidate *candidate) const
{
        size_t idx = crpropa::closestIndex(crpropa::Random::instance().rand(), angleCDF);
        crpropa::Vector3d rv = crpropa::Random::instance().randVector();
        crpropa::Vector3d rotationAxis = candidate->current.getDirection().cross(rv);

        rv = candidate->current.getDirection().getRotated(rotationAxis, M_PI - angle[idx]);
        return rv * scatterVelocity;
}


DirectedFlowOfScatterCenters::DirectedFlowOfScatterCenters(
        const Vector3d &scatterCenterVelocity)
        : __scatterVelocity(scatterCenterVelocity) {}


double DirectedFlowOfScatterCenters::modify(double steplength, Candidate* candidate)
{
        double directionModifier = (-1. * __scatterVelocity.dot(candidate->current.getDirection()) + c_light) / c_light;
        return steplength / directionModifier;
}


DirectedFlowScattering::DirectedFlowScattering(
        crpropa::Vector3d scatterCenterVelocity, double stepLength)
        : __scatterVelocity(scatterCenterVelocity),
          AbstractAccelerationModule(stepLength) {

        // In a directed field of scatter centers, the probability to encounter a
        // scatter center depends on the direction of the candidate.
        StepLengthModifier *mod = new DirectedFlowOfScatterCenters(__scatterVelocity);
        this->add(mod);
}


crpropa::Vector3d DirectedFlowScattering::scatterCenterVelocity(
        crpropa::Candidate *candidate) const { // does not depend on candidate here.
        return __scatterVelocity;
}


QuasiLinearTheory::QuasiLinearTheory(double referenecEnergy,
                                                                         double turbulenceIndex,
                                                                         double minimumRigidity)
        : __referenceEnergy(referenecEnergy), __turbulenceIndex(turbulenceIndex),
          __minimumRigidity(minimumRigidity) {}


double QuasiLinearTheory::modify(double steplength, Candidate* candidate)
{
        if (candidate->current.getRigidity() < __minimumRigidity)
        {
                return steplength * std::pow(__minimumRigidity /
                        (__referenceEnergy / eV), 2. - __turbulenceIndex);
        }
        else
        {
                return steplength * std::pow(candidate->current.getRigidity() /
                        (__referenceEnergy / eV), 2. - __turbulenceIndex);
        }
}


ParticleSplitting::ParticleSplitting(Surface *surface, int      crossingThreshold,
        int numberSplits, double minWeight, std::string counterid)
        : surface(surface), crossingThreshold(crossingThreshold),
          numberSplits(numberSplits), minWeight(minWeight), counterid(counterid){};

void ParticleSplitting::process(Candidate *candidate) const {
        const double currentDistance =
                surface->distance(candidate->current.getPosition());
        const double previousDistance =
                surface->distance(candidate->previous.getPosition());

        if (currentDistance * previousDistance > 0)
                // candidate remains on the same side
                return;

        if (candidate->getWeight() < minWeight)
                return;

        int num_crossings = 1;
        if (candidate->hasProperty(counterid))
                num_crossings = candidate->getProperty(counterid).toInt32() + 1;
        candidate->setProperty(counterid, num_crossings);

        if (num_crossings % crossingThreshold != 0)
                return;

        candidate->updateWeight(1. / numberSplits);

        for (size_t i = 1; i < numberSplits; i++) {
                // No recursive split as the weights of the secondaries created
                // before the split are not affected
                ref_ptr<Candidate> new_candidate = candidate->clone(false);
                new_candidate->parent = candidate;
                uint64_t snr = Candidate::getNextSerialNumber();
                Candidate::setNextSerialNumber(snr + 1);
                new_candidate->setSerialNumber(snr);
                candidate->addSecondary(new_candidate);
        }
};

} // namespace crpropa