C++: a simple geomagnetic field model

A simple geomagnetic field model (including contributions from the Earth's dipole, the effective mirror dipole and the cross-tail current) based on (with minor modifications) [1], [2].

Plot of the magnetic field lines in the magnetosphere.

// gmf_th65.hpp
#if !defined(GMF_TH65_HPP)
#define GMF_TH65_HPP

#include <algorithm>
#include <array>
#include <cmath>
#include <cstddef>
#include <numeric>
#include <ranges>
#include <span>

namespace gmf_th65 { // "gmf" for "geomagnetic field", "th65" for "Taylor and Hones, 1965"

    // 给 std::array 定义矢量加法和归一化;其他一些方便运算也可以定义,但这里用不上
    namespace {
        template <typename T, std::size_t N>
        std::array<T, N> operator+(const std::array<T, N>& lhs, const std::array<T, N>& rhs)
        {
            std::array<T, N> result;
            std::ranges::transform(lhs, rhs, result.begin(), std::plus<T>()); // 性能似乎不如手写循环,make sure you measure it!
            return result;
        }
        template <typename T, std::size_t N>
        void normalize(std::array<T, N>& v)
        {
            // 平方和 ⇔ 自身内积
            T norm_v = std::sqrt(std::inner_product(v.cbegin(), v.cend(), v.cbegin(), static_cast<T>(0)));
            // 逐元素归一化
            std::ranges::transform(v, v.begin(), [norm_v](T x) { return x / norm_v; });
        }
    }

    // 地球的偶极场
    std::array<double, 3> earth_dipole_magnetic_field(double x, double y, double z, double correction_factor = 1.0)
    {
        constexpr double B0 { 3.11e4 }; // dipole 的磁场强度,单位为 nT * RE^3
        double r2 { x * x + y * y + z * z };

        // if (r2 < 1.0) return { 0.0, 0.0, 0.0 }; // 如果在地球内部,磁场强度为零

        double factor { correction_factor * B0 / (r2 * r2 * std::sqrt(r2)) };
        std::array<double, 3> magnetic_field;
        magnetic_field[0] = -3.0 * x * z * factor;
        magnetic_field[1] = -3.0 * y * z * factor;
        magnetic_field[2] = -(2.0 * z * z - x * x - y * y) * factor;

        return magnetic_field;
    }

    // 越尾电流片的磁场
    std::array<double, 3> cross_tail_current_magnetic_field(double x, double y, double z)
    {
        // 电流片在 y 方向上的尺度
        constexpr double width { 20.0 };
        double y_profile { std::exp(-(y * y) / (width * width)) };

        // 电流片在 x 方向上的尺度, 以 RE 为单位
        constexpr double sstart { -6.0 }; // 电流开始出现的位置,这里取为偶极场描述开始出现较大误差处
        constexpr double start { -10.0 }; // 电流片强度刚达到饱和的位置,参考《磁层物理》
        constexpr double end { -40.0 }; // 电流片强度开始衰减的位置,参考《磁层物理》
        constexpr double endd { -200.0 }; // 电流片完全消失的位置,参考《磁层物理》

        double tf { std::tanh((x * 4.0 - 2.0 * (start + sstart)) / (start - sstart)) };
        double tg { std::tanh((x * 4.0 - 2.0 * (end + endd)) / (end - endd)) };

        double f { 0.5 * (1.0 + tf) };
        double g { 0.5 * (1.0 + tg) };

        double x_profile { f * g }; // x 方向电流缓变

        // ---------- Bz 如此这般地选取,使得磁场散度为零 ----------
        // double df_dx { 4.0 / (start - sstart) * (1.0 - tf * tf) };
        // double dg_dx { 4.0 / (end - endd) * (1.0 - tg * tg) };

        // double dX_dx { df_dx * g + f * dg_dx };

        // 电流片在 z 方向上的尺度
        constexpr double half_thick { 0.16 / 2.0 }; // 电流片厚度(约 1000 km,参考《磁层物理》)的一半;Taylor and Hones, 1965 取电流片厚度为 0.5
        // 稳定计算 ln(cosh(z/L))
        double u { z / half_thick };
        // double log_cosh_u {};
        // if (std::abs(u) > 20.0)
        //     log_cosh_u = std::abs(u) - std::log(2.0);  // 避免溢出
        // else
        //     log_cosh_u = std::log(std::cosh(u));

        constexpr double B0 { 25.0 }; // nT,参考《磁层物理》;Taylor and Hones, 1965 取 30 nT, 并且声称这个值影响不大

        double Bx { B0 * x_profile * y_profile * std::tanh(u) };

        // double Bz { -B0 * half_thick * log_cosh_u * y_profile * dX_dx };

        return std::array<double, 3> { Bx, 0.0, 0.0 }; // { Bx, 0.0, Bz }; // 磁场散度为零
    }

    // 总磁场,tpye = void (*)(RealType, std::span<const RealType>, std::span<RealType>) 适合 rk4 调用
    void magnetic_field(double /* t unused */, std::span<const double> position, std::span<double> dydx)
    {
        double x { position[0] };
        double y { position[1] };
        double z { position[2] };

        constexpr double mirror_dipole_distance { 40.0 }; // dipole 跟地心的间距,以 RE 为单位
        constexpr double mirror_dipole_strength_ratio { 28.0 }; // dipole 的磁矩与地球磁矩的比值

        std::array<double, 3> tot_field { earth_dipole_magnetic_field(x, y, z)
                                    + earth_dipole_magnetic_field(x - mirror_dipole_distance, y, z, mirror_dipole_strength_ratio) // 等效源,并非严格的镜像
                                    + cross_tail_current_magnetic_field(x, y, z) };
        normalize(tot_field);
        dydx[0] = tot_field[0];
        dydx[1] = tot_field[1];
        dydx[2] = tot_field[2];
    }
}

#endif // GMF_TH65_HPP

Bibliography

  • [1] H. E. Taylor and E. W. Hones Jr., “Adiabatic motion of auroral particles in a model of the electric and magnetic fields surrounding the Earth,” Journal of Geophysical Research (1896-1977), vol. 70, no. 15, pp. 3605–3628, 1965, doi: https://doi.org/10.1029/JZ070i015p03605.
  • [2] 涂传冶,宗秋刚,周煦之, 日地空间物理学(下册)磁层物理, 2nd ed. 科学出版社, 2020.