Program Listing for File JF12Field.cpp

Return to documentation for file (src/magneticField/JF12Field.cpp)

#include "crpropa/magneticField/JF12Field.h"
#include "crpropa/Units.h"
#include "crpropa/magneticField/turbulentField/SimpleGridTurbulence.h"
#include "crpropa/Random.h"

namespace crpropa {

JF12Field::JF12Field() {
        useRegularField = true;
        useStriatedField = false;
        useTurbulentField = false;
        useDiskField = true;
        useToroidalHaloField = true;
        useXField = true;

        // spiral arm parameters
        pitch = 11.5 * M_PI / 180;
        sinPitch = sin(pitch);
        cosPitch = cos(pitch);
        tanPitch = tan(pitch);
        cotPitch =  1. / tanPitch;
        tan90MinusPitch = tan(M_PI / 2 - pitch);

        rArms[0] = 5.1 * kpc;
        rArms[1] = 6.3 * kpc;
        rArms[2] = 7.1 * kpc;
        rArms[3] = 8.3 * kpc;
        rArms[4] = 9.8 * kpc;
        rArms[5] = 11.4 * kpc;
        rArms[6] = 12.7 * kpc;
        rArms[7] = 15.5 * kpc;

        // regular field parameters
        bRing = 0.1 * muG;
        hDisk = 0.40 * kpc;
        wDisk = 0.27 * kpc;

        bDisk[0] = 0.1 * muG;
        bDisk[1] = 3.0 * muG;
        bDisk[2] = -0.9 * muG;
        bDisk[3] = -0.8 * muG;
        bDisk[4] = -2.0 * muG;
        bDisk[5] = -4.2 * muG;
        bDisk[6] = 0.0 * muG;
        bDisk[7] = 2.7 * muG;

        bNorth = 1.4 * muG;
        bSouth = -1.1 * muG;
        rNorth = 9.22 * kpc;
        rSouth = 17 * kpc;
        wHalo = 0.20 * kpc;
        z0 = 5.3 * kpc;

        bX = 4.6 * muG;
        thetaX0 = 49.0 * M_PI / 180;
        sinThetaX0 = sin(thetaX0);
        cosThetaX0 = cos(thetaX0);
        tanThetaX0 = tan(thetaX0);
        cotThetaX0 = 1. / tanThetaX0;
        rXc = 4.8 * kpc;
        rX = 2.9 * kpc;

        // striated field parameter
        sqrtbeta = sqrt(1.36);

        // turbulent field parameters
        bDiskTurb[0] = 10.81 * muG;
        bDiskTurb[1] = 6.96 * muG;
        bDiskTurb[2] = 9.59 * muG;
        bDiskTurb[3] = 6.96 * muG;
        bDiskTurb[4] = 1.96 * muG;
        bDiskTurb[5] = 16.34 * muG;
        bDiskTurb[6] = 37.29 * muG;
        bDiskTurb[7] = 10.35 * muG;

        bDiskTurb5 = 7.63 * muG;
        zDiskTurb = 0.61 * kpc;

        bHaloTurb = 4.68 * muG;
        rHaloTurb = 10.97 * kpc;
        zHaloTurb = 2.84 * kpc;
}

void JF12Field::randomStriated(int seed) {
        useStriatedField = true;
        int N = 100;
        striatedGrid = new Grid1f(Vector3d(0.), N, 0.1 * kpc);

        Random random;
        if (seed != 0)
                random.seed(seed);

        for (int ix = 0; ix < N; ix++)
                for (int iy = 0; iy < N; iy++)
                        for (int iz = 0; iz < N; iz++) {
                                float &f = striatedGrid->get(ix, iy, iz);
                                f = round(random.rand()) * 2 - 1;
                        }
}

#ifdef CRPROPA_HAVE_FFTW3F
void JF12Field::randomTurbulent(int seed) {
        useTurbulentField = true;
        // turbulent field with Kolmogorov spectrum, B_rms = 1 (will be scaled) and Lc = 60 parsec, and 256 grid points.
        // Note that the inertial range of the turbulence is less than 2 orders of magnitude.
    const double lMin = 8 * parsec;
    const double lMax = 272 * parsec;
    const double Brms = 1;
    const double spacing = 4 * parsec;
    const double grid_n = 256;

    auto spectrum = SimpleTurbulenceSpectrum(Brms, lMin, lMax);
    auto gp = GridProperties(Vector3d(0.), grid_n, spacing);
    auto tf = SimpleGridTurbulence(spectrum, gp, seed);
    turbulentGrid = tf.getGrid();

}
#endif

void JF12Field::setStriatedGrid(ref_ptr<Grid1f> grid) {
        useStriatedField = true;
        striatedGrid = grid;
}

void JF12Field::setTurbulentGrid(ref_ptr<Grid3f> grid) {
        useTurbulentField = true;
        turbulentGrid = grid;
}

ref_ptr<Grid1f> JF12Field::getStriatedGrid() {
        return striatedGrid;
}

ref_ptr<Grid3f> JF12Field::getTurbulentGrid() {
        return turbulentGrid;
}

void JF12Field::setUseRegularField(bool use) {
        useRegularField = use;
}

void JF12Field::setUseDiskField(bool use) {
        useDiskField = use;
}

void JF12Field::setUseToroidalHaloField(bool use) {
        useToroidalHaloField = use;
}

void JF12Field::setUseXField(bool use) {
        useXField = use;
}

void JF12Field::setUseStriatedField(bool use) {
        if ((use) and !(striatedGrid)) {
                KISS_LOG_WARNING << "JF12Field: No striated field set: ignored. Run e.g. randomStriated().";
                return;
        }
        useStriatedField = use;
}

void JF12Field::setUseTurbulentField(bool use) {
        if ((use) and !(turbulentGrid)) {
                KISS_LOG_WARNING << "JF12Field: No turbulent field set: ignored. Run e.g. randomTurbulent().";
                return;
        }
        useTurbulentField = use;
}

bool JF12Field::isUsingRegularField() {
        return useRegularField;
}

bool JF12Field::isUsingDiskField() {
        return useDiskField;
}

bool JF12Field::isUsingToroidalHaloField() {
        return useToroidalHaloField;
}

bool JF12Field::isUsingXField() {
        return useXField;
}

bool JF12Field::isUsingStriatedField() {
        return useStriatedField;
}

bool JF12Field::isUsingTurbulentField() {
        return useTurbulentField;
}

double JF12Field::logisticFunction(const double& x, const double& x0, const double& w) const {
        return 1. / (1. + exp(-2. * (fabs(x) - x0) / w));
}

Vector3d JF12Field::getRegularField(const Vector3d& pos) const {
        Vector3d b(0.);

        double d = pos.getR(); // distance to galactic center

        if (d < 20 * kpc) {
                double r = sqrt(pos.x * pos.x + pos.y * pos.y); // in-plane radius
                double phi = pos.getPhi(); // azimuth
                double sinPhi = sin(phi);
                double cosPhi = cos(phi);

                b += getDiskField(r, pos.z, phi, sinPhi, cosPhi);
                b += getToroidalHaloField(r, pos.z, sinPhi, cosPhi);
                b += getXField(r, pos.z, sinPhi, cosPhi);
        }

        return b;
}

Vector3d JF12Field::getDiskField(const double& r, const double& z, const double& phi, const double& sinPhi, const double& cosPhi) const {
        Vector3d b(0.);
        if (useDiskField) {
                double lfDisk = logisticFunction(z, hDisk, wDisk);
                if (r > 3 * kpc) {
                        double bMag;
                        if (r < 5 * kpc) {
                                // molecular ring
                                bMag = bRing * (5 * kpc / r) * (1 - lfDisk);
                                b.x += -bMag * sinPhi;
                                b.y += bMag * cosPhi;
                        } else {
                                // spiral region
                                double r_negx = r * exp(-(phi - M_PI) / tan90MinusPitch);
                                if (r_negx > rArms[7])
                                        r_negx = r * exp(-(phi + M_PI) / tan90MinusPitch);
                                if (r_negx > rArms[7])
                                        r_negx = r * exp(-(phi + 3 * M_PI) / tan90MinusPitch);

                                for (int i = 7; i >= 0; i--)
                                        if (r_negx < rArms[i])
                                                bMag = bDisk[i];

                                bMag *= (5 * kpc / r) * (1 - lfDisk);
                                b.x += bMag * (sinPitch * cosPhi - cosPitch * sinPhi);
                                b.y += bMag * (sinPitch * sinPhi + cosPitch * cosPhi);
                        }
                }
        }
        return b;
}

Vector3d JF12Field::getToroidalHaloField(const double& r, const double& z, const double& sinPhi, const double& cosPhi) const {
        Vector3d b(0.);

        if (useToroidalHaloField && (r * r + z * z > 1 * kpc * kpc)){

                double lfDisk = logisticFunction(z, hDisk, wDisk);
                double bMagH = exp(-fabs(z) / z0) * lfDisk;

                if (z >= 0)
                        bMagH *= bNorth * (1 - logisticFunction(r, rNorth, wHalo));
                else
                        bMagH *= bSouth * (1 - logisticFunction(r, rSouth, wHalo));
                b.x += -bMagH * sinPhi;
                b.y += bMagH * cosPhi;
        }
        return b;
}

Vector3d JF12Field::getXField(const double& r, const double& z, const double& sinPhi, const double& cosPhi) const {
        Vector3d b(0.);

        if (useXField && (r * r + z * z > 1 * kpc * kpc)){
                double bMagX;
                double sinThetaX, cosThetaX;
                double rp;
                double rc = rXc + fabs(z) / tanThetaX0;
                if (r < rc) {
                        // varying elevation region
                        rp = r * rXc / rc;
                        bMagX = bX * exp(-1 * rp / rX) * pow(rXc / rc, 2.);
                        double thetaX = atan2(fabs(z), (r - rp));
                        if (z == 0)
                                thetaX = M_PI / 2.;
                        sinThetaX = sin(thetaX);
                        cosThetaX = cos(thetaX);
                } else {
                        // constant elevation region
                        rp = r - fabs(z) / tanThetaX0;
                        bMagX = bX * exp(-rp / rX) * (rp / r);
                        sinThetaX = sinThetaX0;
                        cosThetaX = cosThetaX0;
                }
                double zsign = z < 0 ? -1 : 1;
                b.x += zsign * bMagX * cosThetaX * cosPhi;
                b.y += zsign * bMagX * cosThetaX * sinPhi;
                b.z += bMagX * sinThetaX;
        }
        return b;
}

Vector3d JF12Field::getStriatedField(const Vector3d& pos) const {
        return (getRegularField(pos)
                        * (1. + sqrtbeta * striatedGrid->closestValue(pos)));
}

double JF12Field::getTurbulentStrength(const Vector3d& pos) const {
        if (pos.getR() > 20 * kpc)
                return 0;

        double r = sqrt(pos.x * pos.x + pos.y * pos.y); // in-plane radius
        double phi = pos.getPhi(); // azimuth

        // disk
        double bDisk = 0;
        if (r < 5 * kpc) {
                bDisk = bDiskTurb5;
        } else {
                // spiral region
                double r_negx = r * exp(-(phi - M_PI) / tan90MinusPitch);
                if (r_negx > rArms[7])
                        r_negx = r * exp(-(phi + M_PI) / tan90MinusPitch);
                if (r_negx > rArms[7])
                        r_negx = r * exp(-(phi + 3 * M_PI) / tan90MinusPitch);

                for (int i = 7; i >= 0; i--)
                        if (r_negx < rArms[i])
                                bDisk = bDiskTurb[i];

                bDisk *= (5 * kpc) / r;
        }
        bDisk *= exp(-0.5 * pow(pos.z / zDiskTurb, 2));

        // halo
        double bHalo = bHaloTurb * exp(-r / rHaloTurb)
                        * exp(-0.5 * pow(pos.z / zHaloTurb, 2));

        // modulate turbulent field
        return sqrt(pow(bDisk, 2) + pow(bHalo, 2));
}

Vector3d JF12Field::getTurbulentField(const Vector3d& pos) const {
        return (turbulentGrid->interpolate(pos) * getTurbulentStrength(pos));
}

Vector3d JF12Field::getField(const Vector3d& pos) const {
        Vector3d b(0.);
        if (useTurbulentField)
                b += getTurbulentField(pos);
        if (useStriatedField)
                b += getStriatedField(pos);
        else if (useRegularField)
                b += getRegularField(pos);
        return b;
}



PlanckJF12bField::PlanckJF12bField() : JF12Field::JF12Field(){
        // regular field parameters
        bDisk[5] = -3.5 * muG;
        bX = 1.8 * muG;

        // turbulent field parameters;
        bDiskTurb[0] = 3.12 * muG;
        bDiskTurb[1] = 6.24 * muG;
        bDiskTurb[2] = 3.12 * muG;
        bDiskTurb[3] = 6.24 * muG;
        bDiskTurb[4] = 3.12 * muG;
        bDiskTurb[5] = 6.24 * muG;
        bDiskTurb[6] = 3.12 * muG;
        bDiskTurb[7] = 6.24 * muG;

        bDiskTurb5 = 3.90 * muG;

        bHaloTurb = 7.332 * muG;
}

} // namespace crpropa