C++ polytrope implementation

C++ polytrope implementation#

Here’s a C++ implementation of the polytrope ODE integration:

#include <vector>
#include <iostream>
#include <cassert>
#include <cmath>
#include <iomanip>

struct State {
    double theta;
    double dtheta_dxi;
};

class Polytrope {

public:

    std::vector<double> xi;
    std::vector<State> sol;

    double n;

    Polytrope(const double _n=1.5) {
        assert(_n >= 0);
        n = _n;
    }

    State rhs(const double x, const State& s) {

        State f{};

        f.theta = s.dtheta_dxi;

        if (x == 0.0) {
            f.dtheta_dxi = (2.0/3.0) - std::pow(s.theta, n);
        } else {
            f.dtheta_dxi = -2.0 * s.dtheta_dxi / x - std::pow(s.theta, n);
        }

        return f;
    }

    int npts() {
        return xi.size();
    }

    void integrate(const double h0=1.e-2, const double tol=1.e-12) {

        // initial conditions

        State y{1.0, 0.0};

        // storage for the stages

        State tmp{};

        auto h = h0;

        double _xi = 0.0;

        while (h > tol) {

            // 4th order RK integration

            auto k1 = rhs(_xi, y);

            tmp.theta = y.theta + 0.5 * h * k1.theta;
            tmp.dtheta_dxi = y.dtheta_dxi + 0.5 * h * k1.dtheta_dxi;

            auto k2 = rhs(_xi + 0.5*h, tmp);

            tmp.theta = y.theta + 0.5 * h * k2.theta;
            tmp.dtheta_dxi = y.dtheta_dxi + 0.5 * h * k2.dtheta_dxi;

            auto k3 = rhs(_xi + 0.5*h, tmp);

            tmp.theta = y.theta + h * k3.theta;
            tmp.dtheta_dxi = y.dtheta_dxi + h * k3.dtheta_dxi;

            auto k4 = rhs(_xi + h, tmp);

            y.theta += (1./6.) * h * (k1.theta + 2.0*k2.theta +
                                      2.0*k3.theta + k4.theta);
            y.dtheta_dxi += (1./6.) * h * (k1.dtheta_dxi + 2.0*k2.dtheta_dxi +
                                           2.0*k3.dtheta_dxi + k4.dtheta_dxi);

            _xi += h;

            // set the new stepsize--our systems is always convex
            // (theta'' < 0), so the intersection of theta' with the
            // x-axis will always be a conservative estimate of the
            // radius of the star.  Make sure that the stepsize does
            // not take us past that.

            double R_est = _xi - y.theta/y.dtheta_dxi;

            if (_xi + h > R_est) {
                h = -y.theta/y.dtheta_dxi;
            }

            // store the solution:

            xi.push_back(_xi);
            sol.push_back(y);

        }
    }

    double get_xi1() {
        if (!xi.empty()) {
            return xi.back();
        } else {
            return -1;
        }
    }

    double get_minus_xisq_dtheta_dxi() {
        if (!xi.empty() && !sol.empty()) {
            auto xi_last = xi.back();
            auto sol_last = sol.back();

            return -xi_last * xi_last * sol_last.dtheta_dxi;
        } else {
            return -1;
        }
    }
};


int main() {

    for (auto n : {0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5}) {

        Polytrope p(n);
        p.integrate();

        if (n == 0) {
            std::cout << "#" << std::setw(4) << "n"
                      << std::setw(22) << "ξ_1"
                      << std::setw(22) << "-ξ**2 dθ/dξ |_ξ1" << std::endl;
        }
        auto xi1 = p.get_xi1();
        auto minus_xisq_dtheta_dxi = p.get_minus_xisq_dtheta_dxi();

        std::cout << std::setw(4) << n
                  << std::setw(22) << xi1
                  << std::setw(22) << minus_xisq_dtheta_dxi << std::endl;
    }
}