Program Listing for File TF17Field.cpp

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

#include "crpropa/magneticField/TF17Field.h"
#include "crpropa/Units.h"

#include <algorithm>
#include <string>

namespace crpropa {
using namespace std;

TF17Field::TF17Field(TF17DiskModel disk_model_, TF17HaloModel halo_model_) {
    disk_model = disk_model_;
    halo_model = halo_model_;

    useHaloField = true;
    useDiskField = true;

    if ((halo_model == TF17HaloModel::C0) && (disk_model == TF17DiskModel::Ad1)) {
        // disk parameters
        set_r1_disk(3 * kpc);
        set_B1_disk(19.0 * muG);
        set_H_disk(0.055 * kpc);
        set_phi_star_disk(-54 * M_PI / 180);
        set_a_disk(0.9 / kpc / kpc);
        // halo parameters
        set_z1_halo(0);
        set_B1_halo(0.36 * muG);
        set_L_halo(3.0 * kpc);
        set_a_halo(1.17 / kpc / kpc);
        // shared parameters
        set_p0(-7.9 * M_PI / 180);
        set_Hp(5 * kpc);
        set_Lp(50 * kpc); // > 18 kpc

    } else if ((halo_model == TF17HaloModel::C0) && (disk_model == TF17DiskModel::Bd1)) {
        // disk parameters
        set_r1_disk(3 * kpc);
        set_B1_disk(2.0 * muG);
        set_H_disk(0.32 * kpc);
        set_phi_star_disk(153 * M_PI / 180);
        // halo parameters
        set_z1_halo(0);
        set_B1_halo(0.29 * muG);
        set_L_halo(3.4 * kpc);
        set_a_halo(0.88 / kpc / kpc);
        // shared parameters
        set_p0(-7.2 * M_PI / 180);
        set_Hp(9 * kpc);
        set_Lp(50 * kpc); // > 16 kpc

    } else if ((halo_model == TF17HaloModel::C0) && (disk_model == TF17DiskModel::Dd1)) {
        // disk parameters
        set_z1_disk(1.5 * kpc);
        set_B1_disk(0.065 * muG);
        set_L_disk(9.8 * kpc);
        set_phi_star_disk(14 * M_PI / 180);
        // halo parameters
        set_z1_halo(0);
        set_B1_halo(0.18 * muG);
        set_L_halo(4.8 * kpc);
        set_a_halo(0.61 / kpc / kpc);
        // shared parameters
        set_p0(-7.4 * M_PI / 180);
        set_Hp(4.2 * kpc);
        set_Lp(50 * kpc); // > 22 kpc

    } else if ((halo_model == TF17HaloModel::C1) && (disk_model == TF17DiskModel::Ad1)) {
        // disk parameters
        set_r1_disk(3 * kpc);
        set_B1_disk(32.0 * muG);
        set_H_disk(0.054 * kpc);
        set_phi_star_disk(-31 * M_PI / 180);
        set_a_disk(0.031 / kpc / kpc);
        // halo parameters
        set_z1_halo(0);
        set_B1_halo(9.0 * muG);
        set_L_halo(2.1 * kpc);
        set_phi_star_halo(198 * M_PI / 180);
        set_a_halo(0.33 / kpc / kpc);
        // shared parameters
        set_p0(-9.1 * M_PI / 180);
        set_Hp(1.2 * kpc);
        set_Lp(50 * kpc); // > 38 kpc

    } else if ((halo_model == TF17HaloModel::C1) && (disk_model == TF17DiskModel::Bd1)) {
        // disk parameters
        set_r1_disk(3 * kpc);
        set_B1_disk(24 * muG);
        set_H_disk(0.090 * kpc);
        set_phi_star_disk(-34 * M_PI / 180);
        // halo parameters
        set_z1_halo(0);
        set_B1_halo(8.2 * muG);
        set_L_halo(2.2 * kpc);
        set_phi_star_halo(197 * M_PI / 180);
        set_a_halo(0.38 / kpc / kpc);
        // shared parameters
        set_p0(-9.0 * M_PI / 180);
        set_Hp(1.2 * kpc);
        set_Lp(50 * kpc); // > 38 kpc

    } else if ((halo_model == TF17HaloModel::C1) && (disk_model == TF17DiskModel::Dd1)) {
        // disk parameters
        set_z1_disk(1.5 * kpc);
        set_B1_disk(0.40 * muG);
        set_L_disk(2.9 * kpc);
        set_phi_star_disk(120 * M_PI / 180);
        // halo parameters
        set_z1_halo(0);
        set_B1_halo(9.5 * muG);
        set_L_halo(2.1 * kpc);
        set_phi_star_halo(179 * M_PI / 180);
        set_a_halo(0.45 / kpc / kpc);
        // shared parameters
        set_p0(-8.4 * M_PI / 180);
        set_Hp(1.2 * kpc);
        set_Lp(50 * kpc); // > 30 kpc
    }

    epsilon = std::numeric_limits<double>::epsilon();
}

void TF17Field::setUseDiskField(bool use) {     useDiskField = use; }
void TF17Field::setUseHaloField(bool use) { useHaloField = use; }

bool TF17Field::isUsingDiskField() { return useDiskField; }
bool TF17Field::isUsingHaloField() { return useHaloField; }

void TF17Field::set_B1_disk(const double B1){ B1_disk = B1; }
void TF17Field::set_z1_disk(const double z1){ z1_disk = z1; }
void TF17Field::set_r1_disk(const double r1){ r1_disk = r1; }
void TF17Field::set_H_disk(const double H){ H_disk = H; }
void TF17Field::set_L_disk(const double L){ L_disk = L; }
void TF17Field::set_a_disk(const double a){ a_disk = a; }
void TF17Field::set_phi_star_disk(const double phi){ phi_star_disk = phi; }

void TF17Field::set_B1_halo(const double B1){ B1_halo = B1; }
void TF17Field::set_z1_halo(const double z1){ z1_halo = z1; }
void TF17Field::set_L_halo(const double L){ L_halo = L; }
void TF17Field::set_a_halo(const double a){ a_halo = a; }
void TF17Field::set_phi_star_halo(const double phi){ phi_star_halo = phi; }

void TF17Field::set_Hp(const double H){ H_p = H; }
void TF17Field::set_Lp(const double L){ L_p = L; }
void TF17Field::set_p0(const double p0){
    p_0 = p0;
    cot_p0 = cos(p_0) / sin(p_0);
}

string TF17Field::getDiskModel() const {
    string model_name;
    switch (disk_model) {
        case TF17DiskModel::Ad1 : model_name = "Ad1"; break;
        case TF17DiskModel::Bd1 : model_name = "Bd1"; break;
        case TF17DiskModel::Dd1 : model_name = "Dd1"; break;
    }
    return model_name;
}
string TF17Field::getHaloModel() const {
    string model_name;
    switch (halo_model) {
        case TF17HaloModel::C0 : model_name = "C0"; break;
        case TF17HaloModel::C1 : model_name = "C1"; break;
    }
    return model_name;
}


Vector3d TF17Field::getField(const Vector3d& pos) const {
        double r = sqrt(pos.x * pos.x + pos.y * pos.y);  // in-plane radius
        double phi = M_PI - pos.getPhi(); // azimuth in our convention
        // double cosPhi = pos.x / r;
        double cosPhi = cos(phi);
        // double sinPhi = pos.y / r;
        double sinPhi = sin(phi);

        Vector3d b(0.);
        if (useDiskField)
                b += getDiskField(r, pos.z, phi, sinPhi, cosPhi);       // disk field
        if (useHaloField)
                b += getHaloField(r, pos.z, phi, sinPhi, cosPhi);       // halo field
        return b;
}

Vector3d TF17Field::getDiskField(const double& r, const double& z, const double& phi, const double& sinPhi, const double& cosPhi) const {
        Vector3d b(0.);
    double B_r = 0;
    double B_phi = 0;
    double B_z = 0;

    if (disk_model == TF17DiskModel::Ad1) { // ==========================================================
        if (r > r1_disk) {
            double z1_disk_z = (1. + a_disk * r1_disk * r1_disk) / (1. + a_disk * r * r); // z1_disk / z
            // B components in (r, phi, z)
            double B_r0 = radialFieldScale(B1_disk, phi_star_disk, z1_disk_z*z, phi, r, z);
            B_r = (r1_disk / r) * z1_disk_z * B_r0;
            B_z = 2 * a_disk * r1_disk * z1_disk_z*z / (1+ a_disk * r * r) * B_r0;
            B_phi = azimuthalFieldComponent(r, z, B_r, B_z);
        } else {
            // within r = r1_disk, the field lines are straight in direction g_phi + phi_star_disk
            // and thus z = z1
            double phi1_disk = shiftedWindingFunction(r1_disk, z) + phi_star_disk;
            double B_amp = B1_disk * exp(-fabs(z) / H_disk);
            B_r = cos(phi1_disk - phi) * B_amp;
            B_phi = sin(phi1_disk - phi) * B_amp;
        }

    } else if (disk_model == TF17DiskModel::Bd1) { // ===================================================
        // for model Bd1, best fit for n = 2
        if ( r > epsilon ) {
            double r1_disk_r = r1_disk / r;
            double z1_disk_z = 5. / (r1_disk_r*r1_disk_r + 4./sqrt(r1_disk_r)); // z1_disk / z -> remove z dependancy
            double B_r0 = radialFieldScale(B1_disk, phi_star_disk, z1_disk_z*z, phi, r, z);
            B_r = r1_disk_r * z1_disk_z * B_r0;
            B_z = -0.4 * r1_disk_r / r * z1_disk_z* z1_disk_z * z * (r1_disk_r*r1_disk_r - 1./sqrt(r1_disk_r)) * B_r0;
        } else {
            double z1_disk_z = 5. * r*r / (r1_disk*r1_disk); // z1_disk / z -> remove z dependancy
            double B_r0 = radialFieldScale(B1_disk, phi_star_disk, z1_disk_z*z, phi, r, z);
            B_r = 5. * r / r1_disk * B_r0;
            B_z = -10. * z / r1_disk * B_r0;
        }
        B_phi = azimuthalFieldComponent(r, z, B_r, B_z);

    } else if (disk_model == TF17DiskModel::Dd1) { // ===================================================
        // for model Dd1, best fit for n = 0.5
        double z_sign = z >= 0 ? 1. : -1.;
        double z_abs = fabs(z);
        if ( z_abs > epsilon ) {
            double z1_disk_z = z1_disk / z_abs;
            double r1_disk_r = 1.5 / (sqrt(z1_disk_z) + 0.5/z1_disk_z); // r1_disk / r
            double F_r = r1_disk_r*r  <= L_disk ? 1. : exp(1. - r1_disk_r*r/L_disk);
        // simplication of the equation in the cosinus
            double B_z0 = z_sign * B1_disk * F_r * cos(phi - shiftedWindingFunction(r, z) - phi_star_disk);
            B_r = -0.5/1.5 * r1_disk_r * r1_disk_r * r1_disk_r * r / z_abs * (sqrt(z1_disk_z) - 1/z1_disk_z) * B_z0;
            B_z = z_sign * r1_disk_r * r1_disk_r * B_z0;
        } else {
            double z_z1_disk = z_abs / z1_disk;
            double r1_disk_r = 1.5 * sqrt(z_abs / z1_disk); // r1_disk / r
            double F_r = r1_disk_r*r  <= L_disk ? 1. : exp(1. - r1_disk_r*r/L_disk);
            double B_z0 = z_sign * B1_disk * F_r * cos(phi - shiftedWindingFunction(r, z) - phi_star_disk);
            B_r = -1.125 * r / z1_disk * (1 - 2.5 * z_z1_disk * sqrt(z_z1_disk)) * B_z0;
            B_z = z_sign * r1_disk_r * r1_disk_r * B_z0;
        }
        B_phi = azimuthalFieldComponent(r, z, B_r, B_z);
    }

    // Convert to (x, y, z) components
    b.x = - (B_r * cosPhi - B_phi * sinPhi); // flip x-component at the end
    b.y = B_r * sinPhi + B_phi * cosPhi;
    b.z = B_z;
        return b;
}

Vector3d TF17Field::getHaloField(const double& r, const double& z, const double& phi, const double& sinPhi, const double& cosPhi) const {
    int m;

        Vector3d b(0.);
        double r1_halo_r =  (1. + a_halo * z1_halo * z1_halo) / (1. + a_halo * z * z);
        // B components in (r, phi, z)
    double B_z0;

    if (halo_model == TF17HaloModel::C0) { // m = 0
        B_z0 = B1_halo * exp(-r1_halo_r*r / L_halo);
    } else if (halo_model == TF17HaloModel::C1) { // m = 1
        // simplication of the equation in the cosinus
        double phi_prime = phi - shiftedWindingFunction(r, z) - phi_star_halo;
        B_z0 = B1_halo * exp(-r1_halo_r*r / L_halo) * cos(phi_prime);
    }

    // Contrary to article, Br has been rewriten to a little bit by replacing
    // (2 * a * r1**3 * z) / (r**2) by (2 * a * r1**2 * z) / (r * (1+a*z**2))
    // but that is strictly equivalent except we can reintroduce the z1 in the expression via r1
        double B_r = 2 * a_halo * r1_halo_r * r1_halo_r * r * z / (1. + a_halo * z * z) * B_z0;
        double B_z = r1_halo_r * r1_halo_r * B_z0;
        double B_phi = azimuthalFieldComponent(r, z, B_r, B_z);

        // Convert to (x, y, z) components
        b.x = - (B_r * cosPhi - B_phi * sinPhi);        // flip x-component at the end
        b.y = B_r * sinPhi + B_phi * cosPhi;
        b.z = B_z;

        return b;
}

double TF17Field::azimuthalFieldComponent(const double& r, const double& z, const double& B_r, const double& B_z) const {
        double r_ = r / L_p;
    double rscale = r > epsilon ? r_ * exp(-r_) / (1 - exp(-r_)) : 1 - r_/2. - r_*r_/12. ;
        double B_phi = cot_p0 / zscale(z) * rscale * B_r;
    B_phi = B_phi - 2 * z * r / (H_p * H_p) / zscale(z) * shiftedWindingFunction(r, z) * B_z;
        return B_phi;
}

double TF17Field::radialFieldScale(const double& B1, const double& phi_star, const double& z1, const double& phi, const double& r, const double& z) const {
    // simplication of the equation in the cosinus
    double phi_prime = phi - shiftedWindingFunction(r, z) - phi_star;
        // This term occures is parameterizations of models A and B always bisymmetric (m = 1)
        return B1 * exp(-fabs(z1) / H_disk) * cos(phi_prime);
}

double TF17Field::shiftedWindingFunction(const double& r, const double& z) const {
    return cot_p0 * log(1 - exp(-r / L_p) + epsilon) / zscale(z);
}

double TF17Field::zscale(const double& z) const {
        return 1 + z * z / H_p / H_p;
}

} // namespace crpropa