/* * Copyright (c) 2026 Jakub Górnikowski * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see */ #include #include #include #include #include #include #include #include "writers.hpp" #include "vectors.hpp" #include "kutta.hpp" // Source - https://stackoverflow.com/a/4609795 // Posted by user79758, modified by community. See post 'Timeline' for change history // Retrieved 2026-06-07, License - CC BY-SA 4.0 template int sgn(T val) { return (T(0) < val) - (val < T(0)); } double cdot(const std::vector& a, const std::vector& b) { double sum = 0.0; for (size_t i = 0; i < a.size(); i++) { sum += a[i] * b[i]; } return sum; } double norm(const std::vector& a) { return pow(cdot(a, a), 0.5); } std::vector calculate_relative_acceleration( const std::vector& x, const std::vector& y, const double a, // = Gm const double b // = K \eta G^2 m^2 c^-5 ) { std::vector y_dot = - a / pow(norm(x), 3) * x - (8.0 / 5.0) * b / pow(norm(x), 3) * ( - ( 18.0 * pow(norm(y), 2) + (2.0 / 3.0) * (a / norm(x)) - 25.0 * pow(cdot(x, y) / norm(x), 2) ) * (cdot(x, y) / pow(norm(x), 2)) * x + ( 6.0 * pow(norm(y), 2) - 2.0 * (a / norm(x)) - 15.0 * pow(cdot(x, y) / norm(x), 2) ) * y ); return y_dot; } std::vector pulsar(const std::vector& q) { // the q vector contains // Z comprised of x and y vectors, // parameters m and v (4 numbers) /* q vector construction: - x (3 numbers), - y (3 numbers), - v (3 numbers), - m (1 number), - eta (1 number), */ const std::vector x(q.begin(), q.begin() + 3); const std::vector y(q.begin()+3, q.begin() + 6); const double a = q.at(6); const double b = q.at(7); const std::vector new_y = calculate_relative_acceleration( x, y, a, b ); const std::vector zeros = {0.0, 0.0}; std::vector output; output.reserve(8); // Pre-allocate memory output.insert(output.end(), y.begin(), y.end()); output.insert(output.end(), new_y.begin(), new_y.end()); output.insert(output.end(), zeros.begin(), zeros.end()); return output; } std::vector locate_extrema( const std::vector& v ) { if (v.size() <= 1) { return {}; } std::vector extrema; // find points where the differential changes sign int differential_sign = sgn(v.at(1) - v.at(0)); for (size_t i = 0; i < v.size() - 1; i++) { int current_sign = sgn(v.at(i+1) - v.at(i)); if (current_sign != differential_sign) { differential_sign = current_sign; // return the average of the two points extrema.push_back(i); } } return extrema; } int main(void) { // We use strictly SI units: meters, kilograms, seconds. // a = G * m (Units: m^3 / s^2) const double a = 3.753e20 / 1e9; // b = K * eta * (Gm)^2 / c^5 (Units: m * s) // Calculated with K = 1e6, eta = 0.2499, c = 299792458 const double K_over_1e6 = 1e3; const double b = 1.453e4 / 1e3 * K_over_1e6; const std::vector x0 = {7.466e8 / 1e3, 0.0, 0.0}; const std::vector y0 = {0.0, 9.016e5 / 1e3, 0.0}; std::vector initial_condition; initial_condition.reserve(8); initial_condition.insert( initial_condition.end(), x0.begin(), x0.end() ); initial_condition.insert( initial_condition.end(), y0.begin(), y0.end() ); const std::vector model_parameters = {a, b}; initial_condition.insert( initial_condition.end(), model_parameters.begin(), model_parameters.end() ); // ------------------------------------------------- const double below_second_scaling = 1; const double dt = 1.0 * below_second_scaling; const size_t steps = 24 * 3600 * 2 / 3 * (1.0 / (double)below_second_scaling); std::cout << "RK4 time evolution computation:" << std::endl << "Parameters:" << std::endl << ">> " << steps << " temporal steps" << std::endl << ">> " << dt << " of time increment [s]" << std::endl; auto start = std::chrono::high_resolution_clock::now(); // ---------------------------------------------------- std::vector> RK4_matrix = RK4( pulsar, initial_condition, dt, steps ); // ---------------------------------------------------- auto end = std::chrono::high_resolution_clock::now(); std::chrono::duration elapsed = end - start; std::cout << std::endl << "Elapsed time: " << elapsed.count() << " seconds" << std::endl << std::endl; std::vector distances; distances.reserve(steps); for (size_t i = 0; i < RK4_matrix.size(); i++) { const std::vector x( RK4_matrix.at(i).begin(), RK4_matrix.at(i).begin()+3 ); const double distance = norm(x); distances.push_back(distance); } write_1D("distances.csv", distances); const std::vector extrema = locate_extrema( distances ); std::cout << "Distances' size: " << distances.size() << std::endl; std::cout << "Extrema count: " << extrema.size() << std::endl; std::cout << "Extrema indices: " << extrema.size() << std::endl; for (size_t i = 0; i < extrema.size(); i++) { std::cout << extrema.at(i) << std::endl; } const size_t max_1 = extrema.at(0); const size_t min_1 = extrema.at(1); const size_t max_2 = extrema.at(2); const size_t min_2 = extrema.at(3); // we picked the index to the left before, now we average const double averaged_distance_1 = 0.5 * (distances.at(max_1) + distances.at(max_1 + 1)); const double averaged_distance_2 = 0.5 * (distances.at(max_2) + distances.at(max_2 + 1)); std::cout << "Averaged distance 1: " << averaged_distance_1 << std::endl; std::cout << "Averaged distance 2: " << averaged_distance_2 << std::endl; const double distance_change = averaged_distance_1 - averaged_distance_2; // in km const double orbit_time_over_year = 7.752 * 3600.0 / 31557600; std::cout << "Distance shrinkage: " << (distance_change / orbit_time_over_year) / (1e6 * K_over_1e6) * 1000.0 * 0.5 // i think this is double the result << " meters" << std::endl; std::cout << "Time shrinkage: " << (((double)(min_1 - (min_2 - min_1)) * dt) / orbit_time_over_year) / (1e6 * K_over_1e6) << " seconds" << std::endl; return 0; }