From e171a4a61431f3cd120898821c0d64c0e25a814e Mon Sep 17 00:00:00 2001 From: Dario Izzo Date: Wed, 4 Oct 2023 11:03:31 +0200 Subject: [PATCH] round of clang format and throws removed --- benchmark/convert_anomalies_benchmark.cpp | 142 +- benchmark/lambert_problem_benchmark.cpp | 104 +- benchmark/propagate_lagrangian_benchmark.cpp | 264 +- include/kep3/core_astro/constants.hpp | 23 +- include/kep3/core_astro/convert_anomalies.hpp | 253 +- .../kep3/core_astro/convert_julian_dates.hpp | 31 +- include/kep3/core_astro/eq2par2eq.hpp | 9 +- include/kep3/core_astro/ic2eq2ic.hpp | 12 +- include/kep3/core_astro/ic2par2ic.hpp | 9 +- include/kep3/core_astro/kepler_equations.hpp | 112 +- .../kep3/core_astro/propagate_lagrangian.hpp | 15 +- include/kep3/core_astro/special_functions.hpp | 37 +- include/kep3/detail/s11n.hpp | 103 +- include/kep3/detail/type_name.hpp | 72 +- include/kep3/detail/type_traits.hpp | 3 +- include/kep3/detail/typeid_name_extract.hpp | 38 +- include/kep3/epoch.hpp | 377 +- include/kep3/exceptions.hpp | 5 +- include/kep3/lambert_problem.hpp | 113 +- include/kep3/planet.hpp | 527 +- include/kep3/planets/jpl_lp.hpp | 81 +- include/kep3/planets/keplerian.hpp | 105 +- pykep/core.cpp | 118 +- pykep/docstrings.cpp | 148 +- pykep/docstrings.hpp | 3 +- src/core_astro/eq2par2eq.cpp | 93 +- src/core_astro/ic2eq2ic.cpp | 235 +- src/core_astro/ic2par2ic.cpp | 248 +- src/core_astro/propagate_lagrangian.cpp | 394 +- src/detail/type_name.cpp | 39 +- src/epoch.cpp | 111 +- src/lambert_problem.cpp | 608 +- src/planet.cpp | 146 +- src/planets/jpl_lp.cpp | 308 +- src/planets/keplerian.cpp | 268 +- test/catch.hpp | 26501 +++++++++------- test/convert_anomalies_test.cpp | 229 +- test/epoch_test.cpp | 121 +- test/eq2par2eq_test.cpp | 274 +- test/ic2eq2ic_test.cpp | 383 +- test/ic2par2ic_test.cpp | 374 +- test/lambert_problem_test.cpp | 182 +- test/planet_jpl_lp_test.cpp | 347 +- test/planet_keplerian_test.cpp | 312 +- test/planet_test.cpp | 861 +- test/propagate_keplerian_test.cpp | 125 +- test/propagate_lagrangian_test.cpp | 254 +- test/test_helpers.hpp | 39 +- 48 files changed, 18537 insertions(+), 16619 deletions(-) diff --git a/benchmark/convert_anomalies_benchmark.cpp b/benchmark/convert_anomalies_benchmark.cpp index 2675f97d..88097db2 100644 --- a/benchmark/convert_anomalies_benchmark.cpp +++ b/benchmark/convert_anomalies_benchmark.cpp @@ -24,86 +24,86 @@ using std::chrono::microseconds; // In this benchmark we test the speed and accuracy of the Kepler's equation // solvers -void perform_test_speed(double min_ecc, double max_ecc, unsigned N) { - // - // Engines - // - // NOLINTNEXTLINE(cert-msc32-c, cert-msc51-cpp) - std::mt19937 rng_engine(122012203u); - // - // Distributions - // - std::uniform_real_distribution ecc_d(min_ecc, max_ecc); - std::uniform_real_distribution M_d(-1e8, 1e8); +void perform_test_speed(double min_ecc, double max_ecc, unsigned N) +{ + // + // Engines + // + // NOLINTNEXTLINE(cert-msc32-c, cert-msc51-cpp) + std::mt19937 rng_engine(122012203u); + // + // Distributions + // + std::uniform_real_distribution ecc_d(min_ecc, max_ecc); + std::uniform_real_distribution M_d(-1e8, 1e8); - // We generate the random dataset - std::vector eccenricities(N); - std::vector mean_anomalies(N); + // We generate the random dataset + std::vector eccenricities(N); + std::vector mean_anomalies(N); - for (auto i = 0u; i < N; ++i) { - mean_anomalies[i] = M_d(rng_engine); - eccenricities[i] = ecc_d(rng_engine); - } + for (auto i = 0u; i < N; ++i) { + mean_anomalies[i] = M_d(rng_engine); + eccenricities[i] = ecc_d(rng_engine); + } - // We log progress - fmt::print("{:.2f} min_ecc, {:.2f} max_ecc, on {} data points: ", min_ecc, - max_ecc, N); + // We log progress + fmt::print("{:.2f} min_ecc, {:.2f} max_ecc, on {} data points: ", min_ecc, max_ecc, N); - auto start = high_resolution_clock::now(); - for (auto i = 0u; i < N; ++i) { - m2e(mean_anomalies[i], eccenricities[i]); - } - auto stop = high_resolution_clock::now(); - auto duration = duration_cast(stop - start); - fmt::print("{:.3f}s\n", (static_cast(duration.count()) / 1e6)); + auto start = high_resolution_clock::now(); + for (auto i = 0u; i < N; ++i) { + m2e(mean_anomalies[i], eccenricities[i]); + } + auto stop = high_resolution_clock::now(); + auto duration = duration_cast(stop - start); + fmt::print("{:.3f}s\n", (static_cast(duration.count()) / 1e6)); } -void perform_test_accuracy(double min_ecc, double max_ecc, unsigned N) { - // - // Engines - // - // NOLINTNEXTLINE(cert-msc32-c, cert-msc51-cpp) - std::mt19937 rng_engine(122012203u); - // - // Distributions - // - std::uniform_real_distribution ecc_d(min_ecc, max_ecc); - std::uniform_real_distribution M_d(-1e8, 1e8); +void perform_test_accuracy(double min_ecc, double max_ecc, unsigned N) +{ + // + // Engines + // + // NOLINTNEXTLINE(cert-msc32-c, cert-msc51-cpp) + std::mt19937 rng_engine(122012203u); + // + // Distributions + // + std::uniform_real_distribution ecc_d(min_ecc, max_ecc); + std::uniform_real_distribution M_d(-1e8, 1e8); - // We generate the random dataset - std::vector eccenricities(N); - std::vector mean_anomalies(N); + // We generate the random dataset + std::vector eccenricities(N); + std::vector mean_anomalies(N); - for (auto i = 0u; i < N; ++i) { - mean_anomalies[i] = M_d(rng_engine); - eccenricities[i] = ecc_d(rng_engine); - } + for (auto i = 0u; i < N; ++i) { + mean_anomalies[i] = M_d(rng_engine); + eccenricities[i] = ecc_d(rng_engine); + } - // We log progress - fmt::print("{:.2f} min_ecc, {:.2f} max_ecc, on {} data points: ", min_ecc, - max_ecc, N); - std::vector err(N); - for (auto i = 0u; i < N; ++i) { - auto res = e2m(m2e(mean_anomalies[i], eccenricities[i]), eccenricities[i]); - // error is arbitrarily: (|sinM-sinMtrue| +|cosM-cosMtrue|)/2 - err[i] = (std::abs(std::sin(res) - std::sin(mean_anomalies[i])) + - std::abs(std::cos(res) - std::cos(mean_anomalies[i]))) / - 2.; - } - auto max_it = max_element(std::begin(err), std::end(err)); - auto min_it = min_element(std::begin(err), std::end(err)); - auto avg = std::accumulate(err.begin(), err.end(), 0.0) / - static_cast(err.size()); - fmt::print("{:.3e} avg, {:.3e} min, {:.3e} max\n", avg, *min_it, *max_it); + // We log progress + fmt::print("{:.2f} min_ecc, {:.2f} max_ecc, on {} data points: ", min_ecc, max_ecc, N); + std::vector err(N); + for (auto i = 0u; i < N; ++i) { + auto res = e2m(m2e(mean_anomalies[i], eccenricities[i]), eccenricities[i]); + // error is arbitrarily: (|sinM-sinMtrue| +|cosM-cosMtrue|)/2 + err[i] = (std::abs(std::sin(res) - std::sin(mean_anomalies[i])) + + std::abs(std::cos(res) - std::cos(mean_anomalies[i]))) + / 2.; + } + auto max_it = max_element(std::begin(err), std::end(err)); + auto min_it = min_element(std::begin(err), std::end(err)); + auto avg = std::accumulate(err.begin(), err.end(), 0.0) / static_cast(err.size()); + fmt::print("{:.3e} avg, {:.3e} min, {:.3e} max\n", avg, *min_it, *max_it); } -int main() { - fmt::print("\nComputes speed at different eccentricity ranges:\n"); - perform_test_speed(0, 0.5, 1000000); - perform_test_speed(0.5, 0.9, 1000000); - perform_test_speed(0.9, 0.99, 1000000); - fmt::print("\nComputes error at different eccentricity ranges:\n"); - perform_test_accuracy(0, 0.5, 100000); - perform_test_accuracy(0.5, 0.9, 100000); - perform_test_accuracy(0.9, 0.99, 100000); +int main() +{ + fmt::print("\nComputes speed at different eccentricity ranges:\n"); + perform_test_speed(0, 0.5, 1000000); + perform_test_speed(0.5, 0.9, 1000000); + perform_test_speed(0.9, 0.99, 1000000); + fmt::print("\nComputes error at different eccentricity ranges:\n"); + perform_test_accuracy(0, 0.5, 100000); + perform_test_accuracy(0.5, 0.9, 100000); + perform_test_accuracy(0.9, 0.99, 100000); } \ No newline at end of file diff --git a/benchmark/lambert_problem_benchmark.cpp b/benchmark/lambert_problem_benchmark.cpp index 97c3003a..1d07dd5a 100644 --- a/benchmark/lambert_problem_benchmark.cpp +++ b/benchmark/lambert_problem_benchmark.cpp @@ -41,61 +41,61 @@ using std::chrono::duration_cast; using std::chrono::high_resolution_clock; using std::chrono::microseconds; -int main() { - // Number of trials - const unsigned trials = 50000u; +int main() +{ + // Number of trials + const unsigned trials = 50000u; - std::array, trials> r1s{}, r2s{}; - std::array tof{}; - std::array cw{}; - std::array mu{}; + std::array, trials> r1s{}, r2s{}; + std::array tof{}; + std::array cw{}; + std::array mu{}; - // NOLINTNEXTLINE(cert-msc32-c, cert-msc51-cpp) - std::mt19937 rng_engine(122012203u); - std::uniform_int_distribution cw_d(0, 1); - std::uniform_real_distribution r_d(-2, 2); - std::uniform_real_distribution tof_d(2., 40.); - std::uniform_real_distribution mu_d(0.9, 1.1); - unsigned revs_max = 20u; - unsigned long count = 0lu; + // NOLINTNEXTLINE(cert-msc32-c, cert-msc51-cpp) + std::mt19937 rng_engine(122012203u); + std::uniform_int_distribution cw_d(0, 1); + std::uniform_real_distribution r_d(-2, 2); + std::uniform_real_distribution tof_d(2., 40.); + std::uniform_real_distribution mu_d(0.9, 1.1); + unsigned revs_max = 20u; + unsigned long count = 0lu; - for (auto i = 0u; i < trials; ++i) { - // 1 - generate a random problem geometry - r1s[i][0] = r_d(rng_engine); - r1s[i][1] = r_d(rng_engine); - r1s[i][2] = r_d(rng_engine); - r2s[i][0] = r_d(rng_engine); - r2s[i][1] = r_d(rng_engine); - r2s[i][2] = r_d(rng_engine); - tof[i] = tof_d(rng_engine); - cw[i] = static_cast(cw_d(rng_engine)); - mu[i] = mu_d(rng_engine); - } + for (auto i = 0u; i < trials; ++i) { + // 1 - generate a random problem geometry + r1s[i][0] = r_d(rng_engine); + r1s[i][1] = r_d(rng_engine); + r1s[i][2] = r_d(rng_engine); + r2s[i][0] = r_d(rng_engine); + r2s[i][1] = r_d(rng_engine); + r2s[i][2] = r_d(rng_engine); + tof[i] = tof_d(rng_engine); + cw[i] = static_cast(cw_d(rng_engine)); + mu[i] = mu_d(rng_engine); + } - auto start = high_resolution_clock::now(); - for (auto i = 0u; i < trials; ++i) { - // 2 - Solve the lambert problem - kep3::lambert_problem lp(r1s[i], r2s[i], tof[i], mu[i], cw[i], revs_max); - count = count + lp.get_v1().size(); - } - auto stop = high_resolution_clock::now(); - auto duration = duration_cast(stop - start); - fmt::print("Lambert:\n{} solutions computed in {:.3f}s\n", count, - (static_cast(duration.count()) / 1e6)); - fmt::print("Projected number of solutions per second: {}\n", - static_cast(count) / ((static_cast(duration.count()) / 1e6))); + auto start = high_resolution_clock::now(); + for (auto i = 0u; i < trials; ++i) { + // 2 - Solve the lambert problem + kep3::lambert_problem lp(r1s[i], r2s[i], tof[i], mu[i], cw[i], revs_max); + count = count + lp.get_v1().size(); + } + auto stop = high_resolution_clock::now(); + auto duration = duration_cast(stop - start); + fmt::print("Lambert:\n{} solutions computed in {:.3f}s\n", count, (static_cast(duration.count()) / 1e6)); + fmt::print("Projected number of solutions per second: {}\n", + static_cast(count) / ((static_cast(duration.count()) / 1e6))); - count = 0; //reset counter - start = high_resolution_clock::now(); - for (auto i = 0u; i < trials; ++i) { - // 3 - Solve the lambert problem for the singe rev case - kep3::lambert_problem lp(r1s[i], r2s[i], tof[i], mu[i], cw[i], 0u); - count += 1u; - } - stop = high_resolution_clock::now(); - duration = duration_cast(stop - start); - fmt::print("\nLambert (0 revs only):\n{} solutions computed in {:.3f}s\n", count, - (static_cast(duration.count()) / 1e6)); - fmt::print("Projected number of solutions per second: {}\n", - static_cast(count) / ((static_cast(duration.count()) / 1e6))); + count = 0; // reset counter + start = high_resolution_clock::now(); + for (auto i = 0u; i < trials; ++i) { + // 3 - Solve the lambert problem for the singe rev case + kep3::lambert_problem lp(r1s[i], r2s[i], tof[i], mu[i], cw[i], 0u); + count += 1u; + } + stop = high_resolution_clock::now(); + duration = duration_cast(stop - start); + fmt::print("\nLambert (0 revs only):\n{} solutions computed in {:.3f}s\n", count, + (static_cast(duration.count()) / 1e6)); + fmt::print("Projected number of solutions per second: {}\n", + static_cast(count) / ((static_cast(duration.count()) / 1e6))); } \ No newline at end of file diff --git a/benchmark/propagate_lagrangian_benchmark.cpp b/benchmark/propagate_lagrangian_benchmark.cpp index 24c5ae19..e0273950 100644 --- a/benchmark/propagate_lagrangian_benchmark.cpp +++ b/benchmark/propagate_lagrangian_benchmark.cpp @@ -28,151 +28,143 @@ constexpr double pi{boost::math::constants::pi()}; // In this benchmark we test the speed and accuracy of the Lagrangian // propagation solvers -void perform_test_speed( - double min_ecc, double max_ecc, unsigned N, - const std::function, 2> &, double, - double)> &propagate) { - // - // Engines - // - // NOLINTNEXTLINE(cert-msc32-c, cert-msc51-cpp) - std::mt19937 rng_engine(122012203u); - // - // Distributions - // - std::uniform_real_distribution sma_d(0.5, 20.); - std::uniform_real_distribution ecc_d(min_ecc, max_ecc); - std::uniform_real_distribution incl_d(0., pi); - std::uniform_real_distribution Omega_d(0, 2 * pi); - std::uniform_real_distribution omega_d(0., 2 * pi); - std::uniform_real_distribution f_d(0, 2 * pi); - std::uniform_real_distribution tof_d(10., 100.); +void perform_test_speed(double min_ecc, double max_ecc, unsigned N, + const std::function, 2> &, double, double)> &propagate) +{ + // + // Engines + // + // NOLINTNEXTLINE(cert-msc32-c, cert-msc51-cpp) + std::mt19937 rng_engine(122012203u); + // + // Distributions + // + std::uniform_real_distribution sma_d(0.5, 20.); + std::uniform_real_distribution ecc_d(min_ecc, max_ecc); + std::uniform_real_distribution incl_d(0., pi); + std::uniform_real_distribution Omega_d(0, 2 * pi); + std::uniform_real_distribution omega_d(0., 2 * pi); + std::uniform_real_distribution f_d(0, 2 * pi); + std::uniform_real_distribution tof_d(10., 100.); - // We generate the random dataset - std::vector, 2>> pos_vels(N); - std::vector tofs(N); - for (auto i = 0u; i < N; ++i) { - auto ecc = ecc_d(rng_engine); - auto sma = sma_d(rng_engine); - ecc > 1. ? sma = -sma : sma; - double f = pi; - while (std::cos(f) < -1. / ecc && sma < 0.) { - f = f_d(rng_engine); + // We generate the random dataset + std::vector, 2>> pos_vels(N); + std::vector tofs(N); + for (auto i = 0u; i < N; ++i) { + auto ecc = ecc_d(rng_engine); + auto sma = sma_d(rng_engine); + ecc > 1. ? sma = -sma : sma; + double f = pi; + while (std::cos(f) < -1. / ecc && sma < 0.) { + f = f_d(rng_engine); + } + pos_vels[i] = kep3::par2ic({sma, ecc, incl_d(rng_engine), Omega_d(rng_engine), omega_d(rng_engine), f}, 1.); + tofs[i] = tof_d(rng_engine); } - pos_vels[i] = kep3::par2ic({sma, ecc, incl_d(rng_engine), - Omega_d(rng_engine), omega_d(rng_engine), f}, - 1.); - tofs[i] = tof_d(rng_engine); - } - // We log progress - fmt::print("{:.2f} min_ecc, {:.2f} max_ecc, on {} data points: ", min_ecc, - max_ecc, N); + // We log progress + fmt::print("{:.2f} min_ecc, {:.2f} max_ecc, on {} data points: ", min_ecc, max_ecc, N); - auto start = high_resolution_clock::now(); - for (auto i = 0u; i < N; ++i) { - propagate(pos_vels[i], tofs[i], 1.); - } - auto stop = high_resolution_clock::now(); - auto duration = duration_cast(stop - start); - fmt::print("{:.3f}s\n", (static_cast(duration.count()) / 1e6)); + auto start = high_resolution_clock::now(); + for (auto i = 0u; i < N; ++i) { + propagate(pos_vels[i], tofs[i], 1.); + } + auto stop = high_resolution_clock::now(); + auto duration = duration_cast(stop - start); + fmt::print("{:.3f}s\n", (static_cast(duration.count()) / 1e6)); } -void perform_test_accuracy( - double min_ecc, double max_ecc, unsigned N, - const std::function, 2> &, double, - double)> &propagate) { - // - // Engines - // - // NOLINTNEXTLINE(cert-msc32-c, cert-msc51-cpp) - std::mt19937 rng_engine(53534535u); - // - // Distributions - // - std::uniform_real_distribution sma_d(0.5, 10.); - std::uniform_real_distribution ecc_d(min_ecc, max_ecc); - std::uniform_real_distribution incl_d(0., pi); - std::uniform_real_distribution Omega_d(0, 2 * pi); - std::uniform_real_distribution omega_d(0., 2 * pi); - std::uniform_real_distribution f_d(0, 2 * pi); - std::uniform_real_distribution tof_d(10, 100.); +void perform_test_accuracy(double min_ecc, double max_ecc, unsigned N, + const std::function, 2> &, double, double)> &propagate) +{ + // + // Engines + // + // NOLINTNEXTLINE(cert-msc32-c, cert-msc51-cpp) + std::mt19937 rng_engine(53534535u); + // + // Distributions + // + std::uniform_real_distribution sma_d(0.5, 10.); + std::uniform_real_distribution ecc_d(min_ecc, max_ecc); + std::uniform_real_distribution incl_d(0., pi); + std::uniform_real_distribution Omega_d(0, 2 * pi); + std::uniform_real_distribution omega_d(0., 2 * pi); + std::uniform_real_distribution f_d(0, 2 * pi); + std::uniform_real_distribution tof_d(10, 100.); - // We generate the random dataset - std::vector, 2>> pos_vels(N); - std::vector tofs(N); - for (auto i = 0u; i < N; ++i) { - double f = pi; - double ecc = 10.; - double sma = -1.; - while (std::cos(f) < -1. / ecc && sma < 0.) { - ecc = ecc_d(rng_engine); - sma = sma_d(rng_engine); - ecc > 1. ? sma = -sma : sma; - f = f_d(rng_engine); - } + // We generate the random dataset + std::vector, 2>> pos_vels(N); + std::vector tofs(N); + for (auto i = 0u; i < N; ++i) { + double f = pi; + double ecc = 10.; + double sma = -1.; + while (std::cos(f) < -1. / ecc && sma < 0.) { + ecc = ecc_d(rng_engine); + sma = sma_d(rng_engine); + ecc > 1. ? sma = -sma : sma; + f = f_d(rng_engine); + } - pos_vels[i] = kep3::par2ic({sma, ecc, incl_d(rng_engine), - Omega_d(rng_engine), omega_d(rng_engine), f}, - 1.); - tofs[i] = tof_d(rng_engine); - } - // We log progress - fmt::print("{:.2f} min_ecc, {:.2f} max_ecc, on {} data points: ", min_ecc, - max_ecc, N); - std::vector err(N); - auto pos_vels_old(pos_vels); - for (auto i = 0u; i < N; ++i) { - propagate(pos_vels[i], tofs[i], 1.); - propagate(pos_vels[i], -tofs[i], 1.); - err[i] = std::abs(pos_vels[i][0][0] - pos_vels_old[i][0][0]) + - std::abs(pos_vels[i][0][1] - pos_vels_old[i][0][1]) + - std::abs(pos_vels[i][0][2] - pos_vels_old[i][0][2]) + - std::abs(pos_vels[i][1][0] - pos_vels_old[i][1][0]) + - std::abs(pos_vels[i][1][1] - pos_vels_old[i][1][1]) + - std::abs(pos_vels[i][1][2] - pos_vels_old[i][1][2]); - } - auto max_it = max_element(std::begin(err), std::end(err)); - auto min_it = min_element(std::begin(err), std::end(err)); - auto avg = std::accumulate(err.begin(), err.end(), 0.0) / - static_cast(err.size()) / 6.; - fmt::print("{:.3e} avg, {:.3e} min, {:.3e} max\n", avg, *min_it, *max_it); + pos_vels[i] = kep3::par2ic({sma, ecc, incl_d(rng_engine), Omega_d(rng_engine), omega_d(rng_engine), f}, 1.); + tofs[i] = tof_d(rng_engine); + } + // We log progress + fmt::print("{:.2f} min_ecc, {:.2f} max_ecc, on {} data points: ", min_ecc, max_ecc, N); + std::vector err(N); + auto pos_vels_old(pos_vels); + for (auto i = 0u; i < N; ++i) { + propagate(pos_vels[i], tofs[i], 1.); + propagate(pos_vels[i], -tofs[i], 1.); + err[i] = std::abs(pos_vels[i][0][0] - pos_vels_old[i][0][0]) + + std::abs(pos_vels[i][0][1] - pos_vels_old[i][0][1]) + + std::abs(pos_vels[i][0][2] - pos_vels_old[i][0][2]) + + std::abs(pos_vels[i][1][0] - pos_vels_old[i][1][0]) + + std::abs(pos_vels[i][1][1] - pos_vels_old[i][1][1]) + + std::abs(pos_vels[i][1][2] - pos_vels_old[i][1][2]); + } + auto max_it = max_element(std::begin(err), std::end(err)); + auto min_it = min_element(std::begin(err), std::end(err)); + auto avg = std::accumulate(err.begin(), err.end(), 0.0) / static_cast(err.size()) / 6.; + fmt::print("{:.3e} avg, {:.3e} min, {:.3e} max\n", avg, *min_it, *max_it); } -int main() { - fmt::print("\nComputes speed at different eccentricity ranges:\n"); - perform_test_speed(0, 0.5, 1000000, &kep3::propagate_lagrangian); - perform_test_speed(0.5, 0.9, 1000000, &kep3::propagate_lagrangian); - perform_test_speed(0.9, 0.99, 1000000, &kep3::propagate_lagrangian); - perform_test_speed(1.1, 10., 1000000, &kep3::propagate_lagrangian); +int main() +{ + fmt::print("\nComputes speed at different eccentricity ranges:\n"); + perform_test_speed(0, 0.5, 1000000, &kep3::propagate_lagrangian); + perform_test_speed(0.5, 0.9, 1000000, &kep3::propagate_lagrangian); + perform_test_speed(0.9, 0.99, 1000000, &kep3::propagate_lagrangian); + perform_test_speed(1.1, 10., 1000000, &kep3::propagate_lagrangian); - fmt::print("\nComputes error at different eccentricity ranges:\n"); - perform_test_accuracy(0, 0.5, 100000, &kep3::propagate_lagrangian); - perform_test_accuracy(0.5, 0.9, 100000, &kep3::propagate_lagrangian); - perform_test_accuracy(0.9, 0.99, 100000, &kep3::propagate_lagrangian); -// - //fmt::print("\nComputes speed at different eccentricity ranges [Universal " - // "Anomaly]:\n"); - //perform_test_speed(0, 0.5, 1000000, &kep3::propagate_lagrangian_u); - //perform_test_speed(0.5, 0.9, 1000000, &kep3::propagate_lagrangian_u); - //perform_test_speed(0.9, 0.99, 1000000, &kep3::propagate_lagrangian_u); - //perform_test_speed(1.1, 10., 1000000, &kep3::propagate_lagrangian_u); -// - //fmt::print("\nComputes error at different eccentricity ranges [Universal " - // "Anomaly]:\n"); - //perform_test_accuracy(0, 0.5, 100000, &kep3::propagate_lagrangian_u); - //perform_test_accuracy(0.5, 0.9, 100000, &kep3::propagate_lagrangian_u); - //perform_test_accuracy(0.9, 0.99, 100000, &kep3::propagate_lagrangian_u); -// - //fmt::print("\nComputes speed at different eccentricity ranges [keplerian " - // "propagation]:\n"); - //perform_test_speed(0, 0.5, 1000000, &kep3::propagate_keplerian); - //perform_test_speed(0.5, 0.9, 1000000, &kep3::propagate_keplerian); - //perform_test_speed(0.9, 0.99, 1000000, &kep3::propagate_keplerian); -// - //fmt::print("\nComputes error at different eccentricity ranges [keplerian " - // "propagation]:\n"); - //perform_test_accuracy(0, 0.5, 100000, &kep3::propagate_keplerian); - //perform_test_accuracy(0.5, 0.9, 100000, &kep3::propagate_keplerian); - //perform_test_accuracy(0.9, 0.99, 100000, &kep3::propagate_keplerian); + fmt::print("\nComputes error at different eccentricity ranges:\n"); + perform_test_accuracy(0, 0.5, 100000, &kep3::propagate_lagrangian); + perform_test_accuracy(0.5, 0.9, 100000, &kep3::propagate_lagrangian); + perform_test_accuracy(0.9, 0.99, 100000, &kep3::propagate_lagrangian); + // + // fmt::print("\nComputes speed at different eccentricity ranges [Universal " + // "Anomaly]:\n"); + // perform_test_speed(0, 0.5, 1000000, &kep3::propagate_lagrangian_u); + // perform_test_speed(0.5, 0.9, 1000000, &kep3::propagate_lagrangian_u); + // perform_test_speed(0.9, 0.99, 1000000, &kep3::propagate_lagrangian_u); + // perform_test_speed(1.1, 10., 1000000, &kep3::propagate_lagrangian_u); + // + // fmt::print("\nComputes error at different eccentricity ranges [Universal " + // "Anomaly]:\n"); + // perform_test_accuracy(0, 0.5, 100000, &kep3::propagate_lagrangian_u); + // perform_test_accuracy(0.5, 0.9, 100000, &kep3::propagate_lagrangian_u); + // perform_test_accuracy(0.9, 0.99, 100000, &kep3::propagate_lagrangian_u); + // + // fmt::print("\nComputes speed at different eccentricity ranges [keplerian " + // "propagation]:\n"); + // perform_test_speed(0, 0.5, 1000000, &kep3::propagate_keplerian); + // perform_test_speed(0.5, 0.9, 1000000, &kep3::propagate_keplerian); + // perform_test_speed(0.9, 0.99, 1000000, &kep3::propagate_keplerian); + // + // fmt::print("\nComputes error at different eccentricity ranges [keplerian " + // "propagation]:\n"); + // perform_test_accuracy(0, 0.5, 100000, &kep3::propagate_keplerian); + // perform_test_accuracy(0.5, 0.9, 100000, &kep3::propagate_keplerian); + // perform_test_accuracy(0.9, 0.99, 100000, &kep3::propagate_keplerian); } \ No newline at end of file diff --git a/include/kep3/core_astro/constants.hpp b/include/kep3/core_astro/constants.hpp index aab04bd6..d0185637 100644 --- a/include/kep3/core_astro/constants.hpp +++ b/include/kep3/core_astro/constants.hpp @@ -14,25 +14,24 @@ #include -namespace kep3 { +namespace kep3 +{ enum elements_type { - KEP_M, // Keplerian Osculating (with Mean Anomaly) - KEP_F, // Keplerian Osculating (with True Anomaly) - MEQ, // Modified Equinoctial Elements - MEQ_R, // Modified Equinoctial Elements (retrogade) - POSVEL, // position and Velocity + KEP_M, // Keplerian Osculating (with Mean Anomaly) + KEP_F, // Keplerian Osculating (with True Anomaly) + MEQ, // Modified Equinoctial Elements + MEQ_R, // Modified Equinoctial Elements (retrogade) + POSVEL, // position and Velocity }; constexpr double pi = boost::math::constants::pi(); constexpr double half_pi = boost::math::constants::half_pi(); -constexpr double AU = 149597870700.0; // Astronomical Unit (m) -constexpr double CAVENDISH = 73.6687e-11; // Cavendish constant (N M^2 / kg^2) -constexpr double MU_SUN = - 1.32712440018e20; // Sun's gravitational parameter (m^3/s^2 kg) -constexpr double MU_EARTH = - 398600441800000.0; // Earth's gravitational parameter (m^3/s^2 kg) +constexpr double AU = 149597870700.0; // Astronomical Unit (m) +constexpr double CAVENDISH = 73.6687e-11; // Cavendish constant (N M^2 / kg^2) +constexpr double MU_SUN = 1.32712440018e20; // Sun's gravitational parameter (m^3/s^2 kg) +constexpr double MU_EARTH = 398600441800000.0; // Earth's gravitational parameter (m^3/s^2 kg) constexpr double EARTH_VELOCITY = 29784.691831696804; // Earth's velocity. (m/s) constexpr double EARTH_J2 = 1.08262668E-03; constexpr double EARTH_RADIUS = 6378137; // Earth's radius (m) diff --git a/include/kep3/core_astro/convert_anomalies.hpp b/include/kep3/core_astro/convert_anomalies.hpp index a1f8cd47..24020e35 100644 --- a/include/kep3/core_astro/convert_anomalies.hpp +++ b/include/kep3/core_astro/convert_anomalies.hpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -19,177 +20,173 @@ #include #include -namespace kep3 { +namespace kep3 +{ // mean to eccentric (only ellipses) e<1. Preserves the sign and integer number // of revolutions. // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) -inline double m2e(double M, double ecc) { - if (ecc >= 1) { - throw std::domain_error( - "m2e: Eccentric anomaly is not defined for ecc >=1."); - } - // We compute the sin and cos of the mean anomaly which are used also later - // for the initial guess (3rd order expansion of Kepler's equation). - double sinM = std::sin(M), cosM = std::cos(M); - // Here we use the atan2 to recover the mean anomaly in the [0,2pi] range. - // This makes sure that for high value of M no catastrophic cancellation - // occurs, as would be the case using std::fmod(M, 2pi) - double M_cropped = std::atan2(sinM, cosM); - - // The Initial guess follows from a third order expansion of Kepler's - // equation. - double IG = M_cropped + ecc * sinM + ecc * ecc * sinM * cosM + - ecc * ecc * ecc * sinM * (1.5 * cosM * cosM - 0.5); - - const int digits = std::numeric_limits::digits; - std::uintmax_t max_iter = 100u; - - // Newton-raphson or Halley iterates can be used here. Similar performances, - // thus we choose the simplest algorithm. - double sol = boost::math::tools::newton_raphson_iterate( - [M_cropped, ecc](double E) { - return std::make_tuple(kepE(E, M_cropped, ecc), d_kepE(E, ecc)); - }, - IG, IG - kep3::pi, IG + kep3::pi, digits, max_iter); - if (max_iter == 100u) { - throw std::domain_error( - "Maximum number of iterations exceeded when solving Kepler's " - "equation for the eccentric anomaly in m2e."); - } - return sol; +inline double m2e(double M, double ecc) +{ + if (ecc >= 1) { + return std::numeric_limits::quiet_NaN() ; + } + // We compute the sin and cos of the mean anomaly which are used also later + // for the initial guess (3rd order expansion of Kepler's equation). + double sinM = std::sin(M), cosM = std::cos(M); + // Here we use the atan2 to recover the mean anomaly in the [0,2pi] range. + // This makes sure that for high value of M no catastrophic cancellation + // occurs, as would be the case using std::fmod(M, 2pi) + double M_cropped = std::atan2(sinM, cosM); + + // The Initial guess follows from a third order expansion of Kepler's + // equation. + double IG = M_cropped + ecc * sinM + ecc * ecc * sinM * cosM + ecc * ecc * ecc * sinM * (1.5 * cosM * cosM - 0.5); + + const int digits = std::numeric_limits::digits; + std::uintmax_t max_iter = 100u; + + // Newton-raphson or Halley iterates can be used here. Similar performances, + // thus we choose the simplest algorithm. + double sol = boost::math::tools::newton_raphson_iterate( + [M_cropped, ecc](double E) { return std::make_tuple(kepE(E, M_cropped, ecc), d_kepE(E, ecc)); }, IG, + IG - kep3::pi, IG + kep3::pi, digits, max_iter); + if (max_iter == 100u) { + throw std::domain_error("Maximum number of iterations exceeded when solving Kepler's " + "equation for the eccentric anomaly in m2e."); + } + return sol; } // eccentric to mean (only ellipses) e<1 -inline double e2m(double E, double ecc) { - if (ecc >= 1) { - throw std::domain_error( - "e2m: Eccentric anomaly is not defined for ecc >=1."); - } - return (E - ecc * std::sin(E)); +inline double e2m(double E, double ecc) +{ + if (ecc >= 1) { + return std::numeric_limits::quiet_NaN() ; + } + return (E - ecc * std::sin(E)); } // eccentric to true (only ellipses) e<1 (returns in range [-pi,pi]) -inline double e2f(double E, double ecc) { - if (ecc >= 1) { - throw std::domain_error( - "e2f: Eccentric anomaly is not defined for ecc >=1."); - } - return 2 * std::atan(std::sqrt((1 + ecc) / (1 - ecc)) * std::tan(E / 2)); +inline double e2f(double E, double ecc) +{ + if (ecc >= 1) { + return std::numeric_limits::quiet_NaN() ; + } + return 2 * std::atan(std::sqrt((1 + ecc) / (1 - ecc)) * std::tan(E / 2)); } // true to eccentric (only ellipses) e<1 (returns in range [-pi,pi]) -inline double f2e(double f, double ecc) { - if (ecc >= 1) { - throw std::domain_error( - "f2e: Eccentric anomaly is not defined for ecc >=1."); - } - return 2 * std::atan(std::sqrt((1 - ecc) / (1 + ecc)) * std::tan(f / 2)); +inline double f2e(double f, double ecc) +{ + if (ecc >= 1) { + return std::numeric_limits::quiet_NaN() ; + } + return 2 * std::atan(std::sqrt((1 - ecc) / (1 + ecc)) * std::tan(f / 2)); } // mean to true (only ellipses) e<1 (returns in range [-pi,pi]) -inline double m2f(double M, double ecc) { - if (ecc >= 1) { - throw std::domain_error("m2f: Mean anomaly is not defined for ecc >=1."); - }; - return e2f(m2e(M, ecc), ecc); +inline double m2f(double M, double ecc) +{ + if (ecc >= 1) { + return std::numeric_limits::quiet_NaN() ; + }; + return e2f(m2e(M, ecc), ecc); } // true to mean (only ellipses) e<1 (returns in range [-pi,pi]) -inline double f2m(double f, double ecc) { - if (ecc >= 1) { - throw std::domain_error("f2m: Mean anomaly is not defined for ecc >=1."); - }; - return e2m(f2e(f, ecc), ecc); +inline double f2m(double f, double ecc) +{ + if (ecc >= 1) { + return std::numeric_limits::quiet_NaN() ; + }; + return e2m(f2e(f, ecc), ecc); } // gudermannian to true (only hyperbolas) e>1 (returns in range [-pi,pi]) -inline double zeta2f(double f, double ecc) { - if (ecc <= 1) { - throw std::domain_error( - "zeta2f: zeta (Gudermannian) is not defined for ecc <=1."); - }; - return 2 * std::atan(std::sqrt((1 + ecc) / (ecc - 1)) * std::tan(f / 2)); +inline double zeta2f(double f, double ecc) +{ + if (ecc <= 1) { + return std::numeric_limits::quiet_NaN() ; + }; + return 2 * std::atan(std::sqrt((1 + ecc) / (ecc - 1)) * std::tan(f / 2)); } // true to gudermannian (only hyperbolas) e>1 (returns in range [-pi,pi]) -inline double f2zeta(double zeta, double ecc) { - if (ecc <= 1) { - throw std::domain_error( - "f2zeta: zeta (Gudermannian) is not defined for ecc <=1."); - }; - return 2 * std::atan(std::sqrt((ecc - 1) / (1 + ecc)) * std::tan(zeta / 2)); +inline double f2zeta(double zeta, double ecc) +{ + if (ecc <= 1) { + return std::numeric_limits::quiet_NaN() ; + }; + return 2 * std::atan(std::sqrt((ecc - 1) / (1 + ecc)) * std::tan(zeta / 2)); } // mean to hyperbolic (only hyperbolas) e>1. // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) -inline double n2h(double N, double ecc) { - if (ecc <= 1) { - throw std::domain_error( - "n2h: hyperbolic anomaly is not defined for ecc <=1."); - }; - // The Initial guess (TODO(darioizo) improve) - double IG = 1.; - - const int digits = std::numeric_limits::digits; - std::uintmax_t max_iter = 100u; - - // Newton-raphson iterates. - double sol = boost::math::tools::newton_raphson_iterate( - [N, ecc](double H) { - return std::make_tuple(kepH(H, N, ecc), d_kepH(H, ecc)); - }, - IG, IG - 20 * kep3::pi, IG + 20 * kep3::pi, digits, max_iter); - if (max_iter == 100u) { - throw std::domain_error( - "Maximum number of iterations exceeded when solving Kepler's " - "equation for the hyperbolic anomaly in m2h."); - } - return sol; +inline double n2h(double N, double ecc) +{ + if (ecc <= 1) { + return std::numeric_limits::quiet_NaN() ; + }; + // The Initial guess (TODO(darioizo) improve) + double IG = 1.; + + const int digits = std::numeric_limits::digits; + std::uintmax_t max_iter = 100u; + + // Newton-raphson iterates. + double sol = boost::math::tools::newton_raphson_iterate( + [N, ecc](double H) { return std::make_tuple(kepH(H, N, ecc), d_kepH(H, ecc)); }, IG, IG - 20 * kep3::pi, + IG + 20 * kep3::pi, digits, max_iter); + if (max_iter == 100u) { + throw std::domain_error("Maximum number of iterations exceeded when solving Kepler's " + "equation for the hyperbolic anomaly in m2h."); + } + return sol; } // hyperbolic H to hyperbolic mean N (only hyperbolas) e>1 -inline double h2n(double H, double ecc) { - if (ecc <= 1) { - throw std::domain_error( - "h2n: hyperbolic anomaly is not defined for ecc <=1."); - }; - return (ecc * std::sinh(H) - H); +inline double h2n(double H, double ecc) +{ + if (ecc <= 1) { + return std::numeric_limits::quiet_NaN() ; + }; + return (ecc * std::sinh(H) - H); } // hyperbolic H to true (only hyperbolas) e>1 (returns in range [-pi,pi]) -inline double h2f(double H, double ecc) { - if (ecc <= 1) { - throw std::domain_error( - "h2f: hyperbolic anomaly is not defined for ecc <=1."); - }; - return 2 * std::atan(std::sqrt((1 + ecc) / (ecc - 1)) * std::tanh(H / 2)); +inline double h2f(double H, double ecc) +{ + if (ecc <= 1) { + return std::numeric_limits::quiet_NaN() ; + }; + return 2 * std::atan(std::sqrt((1 + ecc) / (ecc - 1)) * std::tanh(H / 2)); } // true to hyperbolic H (only hyperbolas) e>1 (returns in range [-pi,pi]) -inline double f2h(double f, double ecc) { - if (ecc <= 1) { - throw std::domain_error( - "f2h: hyperbolic anomaly is not defined for ecc <=1."); - }; - return 2 * std::atanh(std::sqrt((ecc - 1) / (1 + ecc)) * std::tan(f / 2)); +inline double f2h(double f, double ecc) +{ + if (ecc <= 1) { + return std::numeric_limits::quiet_NaN() ; + }; + return 2 * std::atanh(std::sqrt((ecc - 1) / (1 + ecc)) * std::tan(f / 2)); } // mean hyperbolic to true (only hyperbolas) e>1 (returns in range [-pi,pi]) -inline double n2f(double N, double ecc) { - if (ecc <= 1) { - throw std::domain_error( - "n2f: mean hyperbolic anomaly is not defined for ecc <=1."); - }; - return h2f(n2h(N, ecc), ecc); +inline double n2f(double N, double ecc) +{ + if (ecc <= 1) { + return std::numeric_limits::quiet_NaN() ; + }; + return h2f(n2h(N, ecc), ecc); } // true to mean hyperbolic (only hyperbolas) e>1 (returns in range [-pi,pi]) -inline double f2n(double f, double ecc) { - if (ecc <= 1) { - throw std::domain_error( - "f2n: mean hyperbolic anomaly is not defined for ecc <=1."); - }; - return h2n(f2h(f, ecc), ecc); +inline double f2n(double f, double ecc) +{ + if (ecc <= 1) { + return std::numeric_limits::quiet_NaN() ; + }; + return h2n(f2h(f, ecc), ecc); } } // namespace kep3 diff --git a/include/kep3/core_astro/convert_julian_dates.hpp b/include/kep3/core_astro/convert_julian_dates.hpp index 946dc6d3..be657c2a 100644 --- a/include/kep3/core_astro/convert_julian_dates.hpp +++ b/include/kep3/core_astro/convert_julian_dates.hpp @@ -14,13 +14,30 @@ #include namespace kep3 { - inline double jd2mjd(double in) { return (in - 2400000.5); } - inline double jd2mjd2000(double in) { return (in - 2451544.5); } - inline double mjd2jd(double in) { return (in + 2400000.5); } - inline double mjd2mjd2000(double in) { return (in - 51544); } - inline double mjd20002jd(double in) { return (in + 2451544.5); } - inline double mjd20002mjd(double in){ return (in + 51544); } - +inline double jd2mjd(double in) +{ + return (in - 2400000.5); +} +inline double jd2mjd2000(double in) +{ + return (in - 2451544.5); +} +inline double mjd2jd(double in) +{ + return (in + 2400000.5); +} +inline double mjd2mjd2000(double in) +{ + return (in - 51544); +} +inline double mjd20002jd(double in) +{ + return (in + 2451544.5); +} +inline double mjd20002mjd(double in) +{ + return (in + 51544); +} } // namespace kep3 diff --git a/include/kep3/core_astro/eq2par2eq.hpp b/include/kep3/core_astro/eq2par2eq.hpp index 853763b4..9315e27f 100644 --- a/include/kep3/core_astro/eq2par2eq.hpp +++ b/include/kep3/core_astro/eq2par2eq.hpp @@ -20,13 +20,12 @@ #include -namespace kep3 { +namespace kep3 +{ -kep3_DLL_PUBLIC std::array eq2par(const std::array &eq, - bool retrogade = false); +kep3_DLL_PUBLIC std::array eq2par(const std::array &eq, bool retrogade = false); -kep3_DLL_PUBLIC std::array par2eq(const std::array &par, - bool retrogade = false); +kep3_DLL_PUBLIC std::array par2eq(const std::array &par, bool retrogade = false); } // namespace kep3 #endif // kep3_EQ2PAR2EQ_H \ No newline at end of file diff --git a/include/kep3/core_astro/ic2eq2ic.hpp b/include/kep3/core_astro/ic2eq2ic.hpp index ba337ad2..a056b614 100644 --- a/include/kep3/core_astro/ic2eq2ic.hpp +++ b/include/kep3/core_astro/ic2eq2ic.hpp @@ -20,14 +20,14 @@ #include -namespace kep3 { +namespace kep3 +{ -kep3_DLL_PUBLIC std::array -ic2eq(const std::array, 2> &pos_vel, double mu, - bool retrogade = false); +kep3_DLL_PUBLIC std::array ic2eq(const std::array, 2> &pos_vel, double mu, + bool retrogade = false); -kep3_DLL_PUBLIC std::array, 2> -eq2ic(const std::array &eq, double mu, bool retrogade = false); +kep3_DLL_PUBLIC std::array, 2> eq2ic(const std::array &eq, double mu, + bool retrogade = false); } // namespace kep3 #endif // kep3_IC2EQ2IC_H \ No newline at end of file diff --git a/include/kep3/core_astro/ic2par2ic.hpp b/include/kep3/core_astro/ic2par2ic.hpp index 5fc16788..885e22a6 100644 --- a/include/kep3/core_astro/ic2par2ic.hpp +++ b/include/kep3/core_astro/ic2par2ic.hpp @@ -20,11 +20,10 @@ #include -namespace kep3 { -kep3_DLL_PUBLIC std::array -ic2par(const std::array, 2> &pos_vel, double mu); +namespace kep3 +{ +kep3_DLL_PUBLIC std::array ic2par(const std::array, 2> &pos_vel, double mu); -kep3_DLL_PUBLIC std::array, 2> -par2ic(const std::array &par, double mu); +kep3_DLL_PUBLIC std::array, 2> par2ic(const std::array &par, double mu); } // namespace kep3 #endif // kep3_IC2PAR2IC_H \ No newline at end of file diff --git a/include/kep3/core_astro/kepler_equations.hpp b/include/kep3/core_astro/kepler_equations.hpp index 77a0731e..e9e9258d 100644 --- a/include/kep3/core_astro/kepler_equations.hpp +++ b/include/kep3/core_astro/kepler_equations.hpp @@ -14,93 +14,105 @@ #include -namespace kep3 { +namespace kep3 +{ // In terms of the eccentric anomaly (E) // ------------------------------------------- -inline double kepE(double E, double M, double ecc) { - return (E - ecc * std::sin(E) - M); +inline double kepE(double E, double M, double ecc) +{ + return (E - ecc * std::sin(E) - M); } // Its first derivative -inline double d_kepE(double E, double ecc) { return (1 - ecc * std::cos(E)); } +inline double d_kepE(double E, double ecc) +{ + return (1 - ecc * std::cos(E)); +} // And its second derivative -inline double dd_kepE(double E, double ecc) { return ecc * std::sin(E); } +inline double dd_kepE(double E, double ecc) +{ + return ecc * std::sin(E); +} // ------------------------------------------- // In terms of the hyperbolic anomaly (E) // ------------------------------------------- -inline double kepH(double H, double M, double ecc) { - return (ecc * std::sinh(H) - H - M); +inline double kepH(double H, double M, double ecc) +{ + return (ecc * std::sinh(H) - H - M); } // Its first derivative -inline double d_kepH(double H, double ecc) { return (ecc * std::cosh(H) - 1); } +inline double d_kepH(double H, double ecc) +{ + return (ecc * std::cosh(H) - 1); +} // And its second derivative -inline double dd_kepH(double H, double ecc) { return ecc * std::sinh(H); } +inline double dd_kepH(double H, double ecc) +{ + return ecc * std::sinh(H); +} // ------------------------------------------- // In terms of the eccentric anomaly difference (DE) // ------------------------------------------- -inline double kepDE(double DE, double DM, double sigma0, double sqrta, double a, - double R) { - return -DM + DE + sigma0 / sqrta * (1 - std::cos(DE)) - - (1 - R / a) * std::sin(DE); +inline double kepDE(double DE, double DM, double sigma0, double sqrta, double a, double R) +{ + return -DM + DE + sigma0 / sqrta * (1 - std::cos(DE)) - (1 - R / a) * std::sin(DE); } -inline double d_kepDE(double DE, double sigma0, double sqrta, double a, - double R) { - return 1 + sigma0 / sqrta * std::sin(DE) - (1 - R / a) * std::cos(DE); +inline double d_kepDE(double DE, double sigma0, double sqrta, double a, double R) +{ + return 1 + sigma0 / sqrta * std::sin(DE) - (1 - R / a) * std::cos(DE); } -inline double dd_kepDE(double DE, double sigma0, double sqrta, double a, - double R) { - return sigma0 / sqrta * std::cos(DE) + (1 - R / a) * std::sin(DE); +inline double dd_kepDE(double DE, double sigma0, double sqrta, double a, double R) +{ + return sigma0 / sqrta * std::cos(DE) + (1 - R / a) * std::sin(DE); } // In terms of the hyperbolic anomaly difference (DH) // ------------------------------------------- -inline double kepDH(double DH, double DN, double sigma0, double sqrta, double a, - double R) { - return -DN - DH + sigma0 / sqrta * (std::cosh(DH) - 1) + - (1 - R / a) * std::sinh(DH); +inline double kepDH(double DH, double DN, double sigma0, double sqrta, double a, double R) +{ + return -DN - DH + sigma0 / sqrta * (std::cosh(DH) - 1) + (1 - R / a) * std::sinh(DH); } -inline double d_kepDH(double DH, double sigma0, double sqrta, double a, - double R) { - return -1. + sigma0 / sqrta * std::sinh(DH) + (1 - R / a) * std::cosh(DH); +inline double d_kepDH(double DH, double sigma0, double sqrta, double a, double R) +{ + return -1. + sigma0 / sqrta * std::sinh(DH) + (1 - R / a) * std::cosh(DH); } -inline double dd_kepDH(double DH, double sigma0, double sqrta, double a, - double R) { - return sigma0 / sqrta * std::cosh(DH) + (1 - R / a) * std::sinh(DH); +inline double dd_kepDH(double DH, double sigma0, double sqrta, double a, double R) +{ + return sigma0 / sqrta * std::cosh(DH) + (1 - R / a) * std::sinh(DH); } // In terms of the universal anomaly difference (DS) // ------------------------------------------- -inline double kepDS(const double &DS, const double &DT, const double &r0, - const double &vr0, const double &alpha, const double &mu) { - double S = stumpff_s(alpha * DS * DS); - double C = stumpff_c(alpha * DS * DS); - double retval = -std::sqrt(mu) * DT + r0 * vr0 * DS * DS * C / std::sqrt(mu) + - (1 - alpha * r0) * DS * DS * DS * S + r0 * DS; - return (retval); +inline double kepDS(const double &DS, const double &DT, const double &r0, const double &vr0, const double &alpha, + const double &mu) +{ + double S = stumpff_s(alpha * DS * DS); + double C = stumpff_c(alpha * DS * DS); + double retval + = -std::sqrt(mu) * DT + r0 * vr0 * DS * DS * C / std::sqrt(mu) + (1 - alpha * r0) * DS * DS * DS * S + r0 * DS; + return (retval); } -inline double d_kepDS(const double &DS, const double &r0, const double &vr0, - const double &alpha, const double &mu) { - double S = stumpff_s(alpha * DS * DS); - double C = stumpff_c(alpha * DS * DS); - double retval = r0 * vr0 / std::sqrt(mu) * DS * (1 - alpha * DS * DS * S) + - (1 - alpha * r0) * DS * DS * C + r0; - return (retval); +inline double d_kepDS(const double &DS, const double &r0, const double &vr0, const double &alpha, const double &mu) +{ + double S = stumpff_s(alpha * DS * DS); + double C = stumpff_c(alpha * DS * DS); + double retval = r0 * vr0 / std::sqrt(mu) * DS * (1 - alpha * DS * DS * S) + (1 - alpha * r0) * DS * DS * C + r0; + return (retval); } -inline double dd_kepDS(const double &DS, const double &r0, const double &vr0, - const double &alpha, const double &mu) { - double S = stumpff_s(alpha * DS * DS); - double C = stumpff_c(alpha * DS * DS); - double retval = r0 * vr0 / std::sqrt(mu) * (1 - alpha * DS * DS * C) + - (1 - alpha * r0) * (1 - DS * DS * S); +inline double dd_kepDS(const double &DS, const double &r0, const double &vr0, const double &alpha, const double &mu) +{ + double S = stumpff_s(alpha * DS * DS); + double C = stumpff_c(alpha * DS * DS); + double retval = r0 * vr0 / std::sqrt(mu) * (1 - alpha * DS * DS * C) + (1 - alpha * r0) * (1 - DS * DS * S); - return (retval); + return (retval); } } // namespace kep3 diff --git a/include/kep3/core_astro/propagate_lagrangian.hpp b/include/kep3/core_astro/propagate_lagrangian.hpp index 6cee85e6..5fe58b29 100644 --- a/include/kep3/core_astro/propagate_lagrangian.hpp +++ b/include/kep3/core_astro/propagate_lagrangian.hpp @@ -13,10 +13,10 @@ #include #include +#include -#include - -namespace kep3 { +namespace kep3 +{ /// Lagrangian propagation /** @@ -26,14 +26,11 @@ namespace kep3 { * as the input parameters are all expressed in the same system. */ -kep3_DLL_PUBLIC void propagate_lagrangian(std::array, 2> &pos_vel, - double dt, double mu); +kep3_DLL_PUBLIC void propagate_lagrangian(std::array, 2> &pos_vel, double dt, double mu); -kep3_DLL_PUBLIC void propagate_lagrangian_u(std::array, 2> &pos_vel, - double dt, double mu); +kep3_DLL_PUBLIC void propagate_lagrangian_u(std::array, 2> &pos_vel, double dt, double mu); -kep3_DLL_PUBLIC void propagate_keplerian(std::array, 2> &pos_vel, - double dt, double mu); +kep3_DLL_PUBLIC void propagate_keplerian(std::array, 2> &pos_vel, double dt, double mu); } // namespace kep3 #endif // KEP_TOOLBOX_PROPAGATE_LAGRANGIAN_H \ No newline at end of file diff --git a/include/kep3/core_astro/special_functions.hpp b/include/kep3/core_astro/special_functions.hpp index 508bbf14..c77ee971 100644 --- a/include/kep3/core_astro/special_functions.hpp +++ b/include/kep3/core_astro/special_functions.hpp @@ -12,26 +12,29 @@ #include -namespace kep3 { +namespace kep3 +{ -inline double stumpff_s(const double x) { - if (x > 0) { - return (std::sqrt(x) - std::sin(std::sqrt(x))) / std::pow(std::sqrt(x), 3); - } else if (x < 0) { - return (std::sinh(std::sqrt(-x)) - std::sqrt(-x)) / std::pow(-x, 3. / 2); - } else { - return (1. / 6.); - } +inline double stumpff_s(const double x) +{ + if (x > 0) { + return (std::sqrt(x) - std::sin(std::sqrt(x))) / std::pow(std::sqrt(x), 3); + } else if (x < 0) { + return (std::sinh(std::sqrt(-x)) - std::sqrt(-x)) / std::pow(-x, 3. / 2); + } else { + return (1. / 6.); + } } -inline double stumpff_c(const double x) { - if (x > 0) { - return (1 - std::cos(std::sqrt(x))) / x; - } else if (x < 0) { - return (std::cosh(std::sqrt(-x)) - 1) / (-x); - } else { - return 0.5; - } +inline double stumpff_c(const double x) +{ + if (x > 0) { + return (1 - std::cos(std::sqrt(x))) / x; + } else if (x < 0) { + return (std::cosh(std::sqrt(-x)) - 1) / (-x); + } else { + return 0.5; + } } } // namespace kep3 diff --git a/include/kep3/detail/s11n.hpp b/include/kep3/detail/s11n.hpp index 6878cc30..23454d73 100644 --- a/include/kep3/detail/s11n.hpp +++ b/include/kep3/detail/s11n.hpp @@ -18,10 +18,11 @@ #include #include +#include #include +#include #include #include -#include #include #include #include @@ -29,7 +30,6 @@ #include #include #include -#include #include // Include the archives. @@ -38,74 +38,73 @@ #include #include -namespace kep3::detail { +namespace kep3::detail +{ // Implementation of tuple serialization. -template struct tuple_s11n { - template - static void serialize(Archive &ar, std::tuple &t, unsigned version) { - ar &std::get(t); - tuple_s11n::serialize(ar, t, version); - } +template +struct tuple_s11n { + template + static void serialize(Archive &ar, std::tuple &t, unsigned version) + { + ar &std::get(t); + tuple_s11n::serialize(ar, t, version); + } }; -template <> struct tuple_s11n<0> { - template - static void serialize(Archive &, std::tuple &, unsigned) {} +template <> +struct tuple_s11n<0> { + template + static void serialize(Archive &, std::tuple &, unsigned) + { + } }; } // namespace kep3::detail -namespace boost::serialization { +namespace boost::serialization +{ // Implement serialization for std::tuple. template -inline void serialize(Archive &ar, std::tuple &t, unsigned version) { - kep3::detail::tuple_s11n::serialize(ar, t, version); +inline void serialize(Archive &ar, std::tuple &t, unsigned version) +{ + kep3::detail::tuple_s11n::serialize(ar, t, version); } // Implement serialization for the Mersenne twister engine. -template -inline void save(Archive &ar, - std::mersenne_twister_engine const &e, - unsigned) { - std::ostringstream oss; - // Use the "C" locale. - oss.imbue(std::locale::classic()); - oss << e; - ar << oss.str(); +template +inline void save(Archive &ar, std::mersenne_twister_engine const &e, + unsigned) +{ + std::ostringstream oss; + // Use the "C" locale. + oss.imbue(std::locale::classic()); + oss << e; + ar << oss.str(); } -template -inline void load(Archive &ar, - std::mersenne_twister_engine &e, - unsigned) { - std::istringstream iss; - // Use the "C" locale. - iss.imbue(std::locale::classic()); - std::string tmp; - ar >> tmp; - iss.str(tmp); - iss >> e; +template +inline void load(Archive &ar, std::mersenne_twister_engine &e, + unsigned) +{ + std::istringstream iss; + // Use the "C" locale. + iss.imbue(std::locale::classic()); + std::string tmp; + ar >> tmp; + iss.str(tmp); + iss >> e; } -template -inline void serialize(Archive &ar, - std::mersenne_twister_engine &e, - unsigned version) { - split_free(ar, e, version); +template +inline void serialize(Archive &ar, std::mersenne_twister_engine &e, + unsigned version) +{ + split_free(ar, e, version); } } // namespace boost::serialization diff --git a/include/kep3/detail/type_name.hpp b/include/kep3/detail/type_name.hpp index a25abbf6..9dad4036 100644 --- a/include/kep3/detail/type_name.hpp +++ b/include/kep3/detail/type_name.hpp @@ -16,50 +16,48 @@ #include -namespace kep3::detail { +namespace kep3::detail +{ kep3_DLL_PUBLIC std::string demangle_from_typeid(const char *); // Determine the name of the type T at runtime. -template inline std::string type_name() { - // Get the demangled name without cvref. - auto ret = demangle_from_typeid( - typeid(typename std::remove_cv< - typename std::remove_reference::type>::type) - .name()); +template +inline std::string type_name() +{ + // Get the demangled name without cvref. + auto ret + = demangle_from_typeid(typeid(typename std::remove_cv::type>::type).name()); - // Redecorate it with cv qualifiers. - constexpr unsigned flag = - unsigned(std::is_const::type>::value) + - (unsigned( - std::is_volatile::type>::value) - << 1); - switch (flag) { - case 0u: - // NOTE: handle this explicitly to keep compiler warnings at bay. - break; - case 1u: - ret += " const"; - break; - case 2u: - ret += " volatile"; - break; - case 3u: - ret += " const volatile"; - break; - default: - // you should never go here - throw; - } + // Redecorate it with cv qualifiers. + constexpr unsigned flag = unsigned(std::is_const::type>::value) + + (unsigned(std::is_volatile::type>::value) << 1); + switch (flag) { + case 0u: + // NOTE: handle this explicitly to keep compiler warnings at bay. + break; + case 1u: + ret += " const"; + break; + case 2u: + ret += " volatile"; + break; + case 3u: + ret += " const volatile"; + break; + default: + // you should never go here + throw; + } - // Re-add the reference, if necessary. - if (std::is_lvalue_reference::value) { - ret += " &"; - } else if (std::is_rvalue_reference::value) { - ret += " &&"; - } + // Re-add the reference, if necessary. + if (std::is_lvalue_reference::value) { + ret += " &"; + } else if (std::is_rvalue_reference::value) { + ret += " &&"; + } - return ret; + return ret; } } // namespace kep3::detail diff --git a/include/kep3/detail/type_traits.hpp b/include/kep3/detail/type_traits.hpp index 04e5cdb4..d38d9b24 100644 --- a/include/kep3/detail/type_traits.hpp +++ b/include/kep3/detail/type_traits.hpp @@ -15,7 +15,8 @@ #include #include -namespace kep3::detail { +namespace kep3::detail +{ template using uncvref_t = std::remove_cv_t>; diff --git a/include/kep3/detail/typeid_name_extract.hpp b/include/kep3/detail/typeid_name_extract.hpp index 160cbd9f..e037ce21 100644 --- a/include/kep3/detail/typeid_name_extract.hpp +++ b/include/kep3/detail/typeid_name_extract.hpp @@ -16,7 +16,8 @@ #include -namespace kep3::detail { +namespace kep3::detail +{ // This is an implementation of the extract() functionality // for UDx classes based on the name() of the UDx C++ type, @@ -29,24 +30,23 @@ namespace kep3::detail { // https://github.com/pybind/pybind11/issues/912#issuecomment-310157016 // https://bugs.llvm.org/show_bug.cgi?id=33542 template -inline typename std::conditional::value, const T *, T *>::type -typeid_name_extract(C &class_inst) { - // NOTE: typeid() strips away both reference and cv qualifiers. Thus, - // if T is cv-qualified or a reference type, return nullptr preemptively - // (in any case, extraction cannot be successful in such cases). - if (!std::is_same>::value || std::is_reference::value) { - return nullptr; - } - - if (std::strcmp(class_inst.get_type_index().name(), typeid(T).name()) != 0) { - // The names differ, return null. - return nullptr; - } else { - // The names match, cast to the correct type and return. - return static_cast::value, - const T *, T *>::type>( - class_inst.get_ptr()); - } +inline typename std::conditional::value, const T *, T *>::type typeid_name_extract(C &class_inst) +{ + // NOTE: typeid() strips away both reference and cv qualifiers. Thus, + // if T is cv-qualified or a reference type, return nullptr preemptively + // (in any case, extraction cannot be successful in such cases). + if (!std::is_same>::value || std::is_reference::value) { + return nullptr; + } + + if (std::strcmp(class_inst.get_type_index().name(), typeid(T).name()) != 0) { + // The names differ, return null. + return nullptr; + } else { + // The names match, cast to the correct type and return. + return static_cast::value, const T *, T *>::type>( + class_inst.get_ptr()); + } } } // namespace kep3::detail diff --git a/include/kep3/epoch.hpp b/include/kep3/epoch.hpp index eaec0fe5..c36e7ce1 100644 --- a/include/kep3/epoch.hpp +++ b/include/kep3/epoch.hpp @@ -28,59 +28,62 @@ * that are related to keplerian motions or models. */ -namespace kep3 { +namespace kep3 +{ using namespace std::literals; namespace chr = std::chrono; using uint = unsigned int; -template struct is_duration : std::false_type {}; +template +struct is_duration : std::false_type { +}; template -struct is_duration> : std::true_type {}; +struct is_duration> : std::true_type { +}; template using enable_if_is_duration = std::enable_if_t::value>; struct kep_clock : public chr::system_clock { - /** - * @brief Custom clock. - * Used for constructing epochs with a custom reference point (1 Jan 2000). - * By defining a custom clock, we avoid the overflow - * that std::chrono::system_clock suffers at +/- 292 years. - * To do that, we lower the resolution to 1 us (microsecond), - * which gives the clock a range of +/- 292 thousand years. - * - * NOTE: Adding durations of less than 1 us to an epoch (defined below) - * would not be registered. - * - * NOTE: As of C++20, the standard guarantees that std::chrono::system_clock - * uses the UNIX time reference point, which is midnight on 1 January 1970 - * (1970-01-01T00:00:00). We correct for that here in order to bring the - * reference point forward to midnight on 1 January 2000 - * (2000-01-01T00:00:00), which is 0 MJD2000. - */ - using rep = int_fast64_t; - // Resolution of (1 / 1'000'000)s = 1 us - using period = std::ratio<1, 1'000'000>; - using duration = chr::duration; - using time_point = chr::time_point; - static constexpr bool is_steady = false; - // Number of seconds from midnight on 1 Jan 1970 to midnight on 1 Jan 2000. - static constexpr chr::seconds y2k_offset{946684800s}; - - static constexpr time_point ref_epoch{kep_clock::time_point{} + y2k_offset}; - - static constexpr std::time_t to_time_t(const time_point &t) noexcept { - return static_cast( - chr::duration_cast(t.time_since_epoch() + y2k_offset) - .count()); - } - - static constexpr time_point from_time_t(std::time_t t) noexcept { - return chr::time_point_cast( - time_point(chr::seconds(t) - y2k_offset)); - } + /** + * @brief Custom clock. + * Used for constructing epochs with a custom reference point (1 Jan 2000). + * By defining a custom clock, we avoid the overflow + * that std::chrono::system_clock suffers at +/- 292 years. + * To do that, we lower the resolution to 1 us (microsecond), + * which gives the clock a range of +/- 292 thousand years. + * + * NOTE: Adding durations of less than 1 us to an epoch (defined below) + * would not be registered. + * + * NOTE: As of C++20, the standard guarantees that std::chrono::system_clock + * uses the UNIX time reference point, which is midnight on 1 January 1970 + * (1970-01-01T00:00:00). We correct for that here in order to bring the + * reference point forward to midnight on 1 January 2000 + * (2000-01-01T00:00:00), which is 0 MJD2000. + */ + using rep = int_fast64_t; + // Resolution of (1 / 1'000'000)s = 1 us + using period = std::ratio<1, 1'000'000>; + using duration = chr::duration; + using time_point = chr::time_point; + static constexpr bool is_steady = false; + // Number of seconds from midnight on 1 Jan 1970 to midnight on 1 Jan 2000. + static constexpr chr::seconds y2k_offset{946684800s}; + + static constexpr time_point ref_epoch{kep_clock::time_point{} + y2k_offset}; + + static constexpr std::time_t to_time_t(const time_point &t) noexcept + { + return static_cast(chr::duration_cast(t.time_since_epoch() + y2k_offset).count()); + } + + static constexpr time_point from_time_t(std::time_t t) noexcept + { + return chr::time_point_cast(time_point(chr::seconds(t) - y2k_offset)); + } }; /// epoch class. @@ -95,152 +98,162 @@ struct kep_clock : public chr::system_clock { * - Modified Julian Date 2000 (MJD2000): the number of days passed since * Juanuary 1, 2000 at 00:00 (midnight). */ -class kep3_DLL_PUBLIC epoch { +class kep3_DLL_PUBLIC epoch +{ public: - enum class julian_type { MJD2000, MJD, JD }; - - /** Constructors */ - // Default constructor - epoch(); - - // Constructor for days (as a floating-point value) - explicit epoch(double epoch_in, - julian_type epoch_type = julian_type::MJD2000); - - /** - * Constructs an epoch from a std::chrono::duration. - * The reference point is assumed to be MJD2000. - * \param[in] time The time as a duration. - */ - template > - explicit epoch(const Duration &duration) - : tp{kep_clock::time_point{} + duration} {} - - // Constructor from duration&&) - template > - explicit epoch(Duration &&duration) - : tp{kep_clock::time_point{} + std::forward(duration)} {} - - - // Constructor for datetime broken down into its constituents. - explicit epoch(const int y, const uint mon, const uint d, const int h = 0, - const int min = 0, const int s = 0, const int ms = 0, const int us = 0); - - /* Computing non-Gregorian dates */ - - /** - * @return Number of days since 0 JD (including fractional days). - */ - [[nodiscard]] constexpr double jd() const { - return chr::duration>(tp.time_since_epoch() - kep_clock::y2k_offset + 211813444800s).count(); - } - - /** - * @return Number of days since 0 MJD (including fractional days). - */ - [[nodiscard]] constexpr double mjd() const { - return chr::duration>(tp.time_since_epoch() - kep_clock::y2k_offset + 4453401600s).count(); - } - - /** - * @return Number of days since 0 MJD2000 (including fractional days). - */ - [[nodiscard]] constexpr double mjd2000() const { - return chr::duration>(tp.time_since_epoch() - kep_clock::y2k_offset).count(); - } - - /* Helper functions for constructors */ - static kep_clock::time_point make_tp(const int y, const uint mon, const uint d, const int h = 0, - const int min = 0, const int s = 0, const int ms = 0, const int us = 0); - - static kep_clock::time_point make_tp(double epoch_in, julian_type epoch_type); - - // Conversions - static constexpr kep_clock::time_point tp_from_days(double days); - - // Duration conversions - static constexpr double as_sec(kep_clock::duration &&d) { - return std::chrono::duration(d) - .count(); - } - - // Printing - std::string as_utc_string() const; - static std::string as_utc_string(const kep_clock::time_point& tp); - - /** operator overloads for sum and diff (epoch-days) and comparison - * operators - * **/ - - kep3_DLL_PUBLIC friend std::ostream &operator<<(std::ostream &s, - epoch const &epoch_in); - - template > - epoch &operator+=(const Duration &duration) { - tp += chr::duration_cast(duration); - return *this; - } - - template > - epoch &operator-=(const Duration &duration) { - tp -= chr::duration_cast(duration); - return *this; - } - - kep3_DLL_PUBLIC friend bool operator>(const epoch &c1, const epoch &c2); - kep3_DLL_PUBLIC friend bool operator<(const epoch &c1, const epoch &c2); - kep3_DLL_PUBLIC friend bool operator>=(const epoch &c1, const epoch &c2); - kep3_DLL_PUBLIC friend bool operator<=(const epoch &c1, const epoch &c2); - kep3_DLL_PUBLIC friend bool operator==(const epoch &c1, const epoch &c2); - kep3_DLL_PUBLIC friend bool operator!=(const epoch &c1, const epoch &c2); - - template > - epoch operator+(const Duration &duration) { - return epoch(tp + chr::duration_cast(duration)); - } - - template > - epoch operator-(const Duration &duration) { - return epoch(tp - chr::duration_cast(duration)); - } - - static constexpr auto days(const double value) { - return chr::duration_cast( - chr::duration>(value)); - } - - static constexpr auto sec(const double value) { - return chr::duration_cast( - chr::duration>(value)); - } - - kep3_DLL_PUBLIC friend kep_clock::duration operator-(const epoch &lhs, - const epoch &rhs); - - private: - - // Constructor for const time_point&) - explicit epoch(const kep_clock::time_point &time_point); - - // Constructor for const time_point&&) - explicit epoch(kep_clock::time_point &&time_point); - - // Serialization code - friend class boost::serialization::access; - template void serialize(Archive &ar, const uint) { - ar &boost::serialization::make_binary_object(&tp, sizeof(tp)); - } - // Serialization code (END) - - // Time point relative to 1 Jan 2000 (MJD2000) - kep_clock::time_point tp; + enum class julian_type { MJD2000, MJD, JD }; + + /** Constructors */ + // Default constructor + epoch(); + + // Constructor for days (as a floating-point value) + explicit epoch(double epoch_in, julian_type epoch_type = julian_type::MJD2000); + + /** + * Constructs an epoch from a std::chrono::duration. + * The reference point is assumed to be MJD2000. + * \param[in] time The time as a duration. + */ + template > + explicit epoch(const Duration &duration) : tp{kep_clock::time_point{} + duration} + { + } + + // Constructor from duration&&) + template > + explicit epoch(Duration &&duration) : tp{kep_clock::time_point{} + std::forward(duration)} + { + } + + // Constructor for datetime broken down into its constituents. + explicit epoch(const int y, const uint mon, const uint d, const int h = 0, const int min = 0, const int s = 0, + const int ms = 0, const int us = 0); + + /* Computing non-Gregorian dates */ + + /** + * @return Number of days since 0 JD (including fractional days). + */ + [[nodiscard]] constexpr double jd() const + { + return chr::duration>(tp.time_since_epoch() - kep_clock::y2k_offset + 211813444800s) + .count(); + } + + /** + * @return Number of days since 0 MJD (including fractional days). + */ + [[nodiscard]] constexpr double mjd() const + { + return chr::duration>(tp.time_since_epoch() - kep_clock::y2k_offset + 4453401600s) + .count(); + } + + /** + * @return Number of days since 0 MJD2000 (including fractional days). + */ + [[nodiscard]] constexpr double mjd2000() const + { + return chr::duration>(tp.time_since_epoch() - kep_clock::y2k_offset).count(); + } + + /* Helper functions for constructors */ + static kep_clock::time_point make_tp(const int y, const uint mon, const uint d, const int h = 0, const int min = 0, + const int s = 0, const int ms = 0, const int us = 0); + + static kep_clock::time_point make_tp(double epoch_in, julian_type epoch_type); + + // Conversions + static constexpr kep_clock::time_point tp_from_days(double days); + + // Duration conversions + static constexpr double as_sec(kep_clock::duration &&d) + { + return std::chrono::duration(d).count(); + } + + // Printing + std::string as_utc_string() const; + static std::string as_utc_string(const kep_clock::time_point &tp); + + /** operator overloads for sum and diff (epoch-days) and comparison + * operators + * **/ + + kep3_DLL_PUBLIC friend std::ostream &operator<<(std::ostream &s, epoch const &epoch_in); + + template > + epoch &operator+=(const Duration &duration) + { + tp += chr::duration_cast(duration); + return *this; + } + + template > + epoch &operator-=(const Duration &duration) + { + tp -= chr::duration_cast(duration); + return *this; + } + + kep3_DLL_PUBLIC friend bool operator>(const epoch &c1, const epoch &c2); + kep3_DLL_PUBLIC friend bool operator<(const epoch &c1, const epoch &c2); + kep3_DLL_PUBLIC friend bool operator>=(const epoch &c1, const epoch &c2); + kep3_DLL_PUBLIC friend bool operator<=(const epoch &c1, const epoch &c2); + kep3_DLL_PUBLIC friend bool operator==(const epoch &c1, const epoch &c2); + kep3_DLL_PUBLIC friend bool operator!=(const epoch &c1, const epoch &c2); + + template > + epoch operator+(const Duration &duration) + { + return epoch(tp + chr::duration_cast(duration)); + } + + template > + epoch operator-(const Duration &duration) + { + return epoch(tp - chr::duration_cast(duration)); + } + + static constexpr auto days(const double value) + { + return chr::duration_cast(chr::duration>(value)); + } + + static constexpr auto sec(const double value) + { + return chr::duration_cast(chr::duration>(value)); + } + + kep3_DLL_PUBLIC friend kep_clock::duration operator-(const epoch &lhs, const epoch &rhs); + +private: + // Constructor for const time_point&) + explicit epoch(const kep_clock::time_point &time_point); + + // Constructor for const time_point&&) + explicit epoch(kep_clock::time_point &&time_point); + + // Serialization code + friend class boost::serialization::access; + template + void serialize(Archive &ar, const uint) + { + ar &boost::serialization::make_binary_object(&tp, sizeof(tp)); + } + // Serialization code (END) + + // Time point relative to 1 Jan 2000 (MJD2000) + kep_clock::time_point tp; }; -kep3_DLL_PUBLIC std::ostream &operator<<(std::ostream &s, - const epoch &epoch_in); +kep3_DLL_PUBLIC std::ostream &operator<<(std::ostream &s, const epoch &epoch_in); } // end of namespace kep3 -template <> struct fmt::formatter : fmt::ostream_formatter {}; +template <> +struct fmt::formatter : fmt::ostream_formatter { +}; #endif // kep3_EPOCH_HPP diff --git a/include/kep3/exceptions.hpp b/include/kep3/exceptions.hpp index cd72af39..24a7b3af 100644 --- a/include/kep3/exceptions.hpp +++ b/include/kep3/exceptions.hpp @@ -15,9 +15,10 @@ #include #include -namespace kep3 { +namespace kep3 +{ struct kep3_DLL_PUBLIC not_implemented_error final : std::runtime_error { - using std::runtime_error::runtime_error; + using std::runtime_error::runtime_error; }; } // namespace kep3 diff --git a/include/kep3/lambert_problem.hpp b/include/kep3/lambert_problem.hpp index 316eddcc..bb42352b 100644 --- a/include/kep3/lambert_problem.hpp +++ b/include/kep3/lambert_problem.hpp @@ -18,7 +18,8 @@ #include #include -namespace kep3 { +namespace kep3 +{ /// Lambert Problem /** @@ -44,71 +45,69 @@ namespace kep3 { class kep3_DLL_PUBLIC lambert_problem; // Streaming operator for the class kep_toolbox::lambert_problem. -kep3_DLL_PUBLIC std::ostream &operator<<(std::ostream &, - const lambert_problem &); +kep3_DLL_PUBLIC std::ostream &operator<<(std::ostream &, const lambert_problem &); -class kep3_DLL_PUBLIC lambert_problem { - static const std::array default_r1; - static const std::array default_r2; +class kep3_DLL_PUBLIC lambert_problem +{ + static const std::array default_r1; + static const std::array default_r2; public: - friend kep3_DLL_PUBLIC std::ostream &operator<<(std::ostream &, - const lambert_problem &); - explicit lambert_problem(const std::array &r1 = default_r1, - const std::array &r2 = default_r2, - double tof = kep3::pi / 2, double mu = 1., - bool cw = false, unsigned multi_revs = 5); - [[nodiscard]] const std::vector> &get_v1() const; - [[nodiscard]] const std::vector> &get_v2() const; - [[nodiscard]] const std::array &get_r1() const; - [[nodiscard]] const std::array &get_r2() const; - [[nodiscard]] const double &get_tof() const; - [[nodiscard]] const double &get_mu() const; - [[nodiscard]] const std::vector &get_x() const; - [[nodiscard]] const std::vector &get_iters() const; - [[nodiscard]] unsigned get_Nmax() const; + friend kep3_DLL_PUBLIC std::ostream &operator<<(std::ostream &, const lambert_problem &); + explicit lambert_problem(const std::array &r1 = default_r1, const std::array &r2 = default_r2, + double tof = kep3::pi / 2, double mu = 1., bool cw = false, unsigned multi_revs = 5); + [[nodiscard]] const std::vector> &get_v1() const; + [[nodiscard]] const std::vector> &get_v2() const; + [[nodiscard]] const std::array &get_r1() const; + [[nodiscard]] const std::array &get_r2() const; + [[nodiscard]] const double &get_tof() const; + [[nodiscard]] const double &get_mu() const; + [[nodiscard]] const std::vector &get_x() const; + [[nodiscard]] const std::vector &get_iters() const; + [[nodiscard]] unsigned get_Nmax() const; private: - unsigned householder(double, double &, unsigned, double, unsigned) const; - void dTdx(double &, double &, double &, double, double) const; - void x2tof(double &tof, double x0, unsigned N) const; - void x2tof2(double &tof, double x0, unsigned N) const ; - [[nodiscard]] double hypergeometricF(double z, double tol) const; - friend class boost::serialization::access; - template void serialize(Archive &ar, const unsigned int) { - ar &m_r1; - ar &m_r2; - ar &m_tof; - ar &m_mu; - ar &m_v1; - ar &m_v2; - ar &m_iters; - ar &m_x; - ar &m_s; - ar &m_c; - ar &m_lambda; - ar &m_iters; - ar &m_Nmax; - ar &m_has_converged; - ar &m_multi_revs; - } + unsigned householder(double, double &, unsigned, double, unsigned) const; + void dTdx(double &, double &, double &, double, double) const; + void x2tof(double &tof, double x0, unsigned N) const; + void x2tof2(double &tof, double x0, unsigned N) const; + [[nodiscard]] double hypergeometricF(double z, double tol) const; + friend class boost::serialization::access; + template + void serialize(Archive &ar, const unsigned int) + { + ar & m_r1; + ar & m_r2; + ar & m_tof; + ar & m_mu; + ar & m_v1; + ar & m_v2; + ar & m_iters; + ar & m_x; + ar & m_s; + ar & m_c; + ar & m_lambda; + ar & m_iters; + ar & m_Nmax; + ar & m_has_converged; + ar & m_multi_revs; + } - std::array m_r1, m_r2; - double m_tof; - double m_mu; - std::vector> m_v1; - std::vector> m_v2; - std::vector m_iters; - std::vector m_x; - double m_s, m_c, m_lambda; - unsigned m_Nmax; - bool m_has_converged; - unsigned m_multi_revs; + std::array m_r1, m_r2; + double m_tof; + double m_mu; + std::vector> m_v1; + std::vector> m_v2; + std::vector m_iters; + std::vector m_x; + double m_s, m_c, m_lambda; + unsigned m_Nmax; + bool m_has_converged; + unsigned m_multi_revs; }; // Streaming operator for the class kep3::lambert_problem. -kep3_DLL_PUBLIC std::ostream &operator<<(std::ostream &, - const lambert_problem &); +kep3_DLL_PUBLIC std::ostream &operator<<(std::ostream &, const lambert_problem &); } // namespace kep3 diff --git a/include/kep3/planet.hpp b/include/kep3/planet.hpp index 3fdf3038..ff197c39 100644 --- a/include/kep3/planet.hpp +++ b/include/kep3/planet.hpp @@ -29,30 +29,25 @@ #include #include -#define kep3_S11N_PLANET_EXPORT_KEY(PLA) \ - BOOST_CLASS_EXPORT_KEY2(kep3::detail::planet_inner, "udpla " #PLA) \ - BOOST_CLASS_TRACKING(kep3::detail::planet_inner, \ - boost::serialization::track_never) +#define kep3_S11N_PLANET_EXPORT_KEY(PLA) \ + BOOST_CLASS_EXPORT_KEY2(kep3::detail::planet_inner, "udpla " #PLA) \ + BOOST_CLASS_TRACKING(kep3::detail::planet_inner, boost::serialization::track_never) -#define kep3_S11N_PLANET_IMPLEMENT(PLA) \ - BOOST_CLASS_EXPORT_IMPLEMENT(kep3::detail::planet_inner) +#define kep3_S11N_PLANET_IMPLEMENT(PLA) BOOST_CLASS_EXPORT_IMPLEMENT(kep3::detail::planet_inner) -#define kep3_S11N_PLANET_EXPORT(PLA) \ - kep3_S11N_PLANET_EXPORT_KEY(PLA) kep3_S11N_PLANET_IMPLEMENT(PLA) +#define kep3_S11N_PLANET_EXPORT(PLA) kep3_S11N_PLANET_EXPORT_KEY(PLA) kep3_S11N_PLANET_IMPLEMENT(PLA) -namespace kep3::detail { +namespace kep3::detail +{ // Type traits to detect whether user classes have certain methods implemented. // This macro assembles the necessary boilerplate to detect the generic getter // method double udpla_get_NAME() -#define UDPLA_HAS_GET(NAME) \ - template \ - using udpla_get_##NAME##_t = \ - decltype(std::declval>() \ - .get_##NAME()); \ - template \ - inline constexpr bool udpla_has_get_##NAME##_v = \ - std::is_same_v, double> +#define UDPLA_HAS_GET(NAME) \ + template \ + using udpla_get_##NAME##_t = decltype(std::declval>().get_##NAME()); \ + template \ + inline constexpr bool udpla_has_get_##NAME##_v = std::is_same_v, double> UDPLA_HAS_GET(mu_central_body); UDPLA_HAS_GET(mu_self); @@ -63,304 +58,322 @@ UDPLA_HAS_GET(safe_radius); // std::array, 2> eph(const epoch&) // method template -using udpla_eph_t = - decltype(std::declval>().eph( - std::declval())); +using udpla_eph_t = decltype(std::declval>().eph(std::declval())); template -using udpla_has_eph = std::is_same, - std::array, 2>>; +using udpla_has_eph = std::is_same, std::array, 2>>; // udpla_has_get_name_v is True if T has the method: // std::string get_name() template -using udpla_get_name_t = - decltype(std::declval>().get_name()); +using udpla_get_name_t = decltype(std::declval>().get_name()); template -inline constexpr bool udpla_has_get_name_v = - std::is_same_v, std::string>; +inline constexpr bool udpla_has_get_name_v = std::is_same_v, std::string>; // udpla_has_get_extra_info_v is True if T has the method: // std::string get_extra_info() template -using udpla_get_extra_info_t = - decltype(std::declval>() - .get_extra_info()); +using udpla_get_extra_info_t = decltype(std::declval>().get_extra_info()); template -inline constexpr bool udpla_has_get_extra_info_v = - std::is_same_v, std::string>; +inline constexpr bool udpla_has_get_extra_info_v = std::is_same_v, std::string>; // udpla_has_period_v is True if T has the method: // double period(const epoch&) template -using udpla_period_t = - decltype(std::declval>().period( - std::declval())); +using udpla_period_t + = decltype(std::declval>().period(std::declval())); template -inline constexpr bool udpla_has_period_v = - std::is_same_v, double>; +inline constexpr bool udpla_has_period_v = std::is_same_v, double>; // This defines the main interface for a class to be type erased into a kep3 // planet struct kep3_DLL_PUBLIC_INLINE_CLASS planet_inner_base { - planet_inner_base() = default; - planet_inner_base(const planet_inner_base &) = delete; - planet_inner_base(planet_inner_base &&) noexcept = delete; - planet_inner_base &operator=(const planet_inner_base &) = delete; - planet_inner_base &operator=(planet_inner_base &&) noexcept = delete; - virtual ~planet_inner_base() = default; - - [[nodiscard]] virtual std::unique_ptr clone() const = 0; - - [[nodiscard]] virtual std::type_index get_type_index() const = 0; - [[nodiscard]] virtual const void *get_ptr() const = 0; - virtual void *get_ptr() = 0; - - // mandatory methods - [[nodiscard]] virtual std::array, 2> - eph(const epoch &) const = 0; - // optional methods with default implementations - [[nodiscard]] virtual std::string get_name() const = 0; - [[nodiscard]] virtual std::string get_extra_info() const = 0; - [[nodiscard]] virtual double get_mu_central_body() const = 0; - [[nodiscard]] virtual double get_mu_self() const = 0; - [[nodiscard]] virtual double get_radius() const = 0; - [[nodiscard]] virtual double get_safe_radius() const = 0; - [[nodiscard]] virtual double period(const kep3::epoch &) const = 0; + planet_inner_base() = default; + planet_inner_base(const planet_inner_base &) = delete; + planet_inner_base(planet_inner_base &&) noexcept = delete; + planet_inner_base &operator=(const planet_inner_base &) = delete; + planet_inner_base &operator=(planet_inner_base &&) noexcept = delete; + virtual ~planet_inner_base() = default; + + [[nodiscard]] virtual std::unique_ptr clone() const = 0; + + [[nodiscard]] virtual std::type_index get_type_index() const = 0; + [[nodiscard]] virtual const void *get_ptr() const = 0; + virtual void *get_ptr() = 0; + + // mandatory methods + [[nodiscard]] virtual std::array, 2> eph(const epoch &) const = 0; + // optional methods with default implementations + [[nodiscard]] virtual std::string get_name() const = 0; + [[nodiscard]] virtual std::string get_extra_info() const = 0; + [[nodiscard]] virtual double get_mu_central_body() const = 0; + [[nodiscard]] virtual double get_mu_self() const = 0; + [[nodiscard]] virtual double get_radius() const = 0; + [[nodiscard]] virtual double get_safe_radius() const = 0; + [[nodiscard]] virtual double period(const kep3::epoch &) const = 0; private: - // Serialization. - friend class boost::serialization::access; - template void serialize(Archive &, unsigned) {} + // Serialization. + friend class boost::serialization::access; + template + void serialize(Archive &, unsigned) + { + } }; template struct kep3_DLL_PUBLIC_INLINE_CLASS planet_inner final : planet_inner_base { - T m_value; - - // We just need the def ctor, delete everything else. - planet_inner() = default; - planet_inner(const planet_inner &) = delete; - planet_inner(planet_inner &&) = delete; - planet_inner &operator=(const planet_inner &) = delete; - planet_inner &operator=(planet_inner &&) = delete; - ~planet_inner() final = default; - - // Constructors from T (copy and move variants). - explicit planet_inner(const T &x) : m_value(x) {} - explicit planet_inner(T &&x) : m_value(std::move(x)) {} - - // The clone method, used in the copy constructor of planet. - [[nodiscard]] std::unique_ptr clone() const final { - return std::make_unique(m_value); - } - - // Get the type at runtime. - [[nodiscard]] std::type_index get_type_index() const final { - return std::type_index{typeid(T)}; - } - - // Raw getters for the internal instance. - [[nodiscard]] const void *get_ptr() const final { return &m_value; } - void *get_ptr() final { return &m_value; } - - // Mandatory methods. - [[nodiscard]] std::array, 2> - eph(const epoch &ep) const final { - return m_value.eph(ep); - } - // optional methods with default implementations - // these require added boiler plate as to detect whether they have been - // implemented by the user class. - [[nodiscard]] std::string get_name() const final { - if constexpr (udpla_has_get_name_v) { - return m_value.get_name(); - } else { - return detail::type_name(); + T m_value; + + // We just need the def ctor, delete everything else. + planet_inner() = default; + planet_inner(const planet_inner &) = delete; + planet_inner(planet_inner &&) = delete; + planet_inner &operator=(const planet_inner &) = delete; + planet_inner &operator=(planet_inner &&) = delete; + ~planet_inner() final = default; + + // Constructors from T (copy and move variants). + explicit planet_inner(const T &x) : m_value(x) {} + explicit planet_inner(T &&x) : m_value(std::move(x)) {} + + // The clone method, used in the copy constructor of planet. + [[nodiscard]] std::unique_ptr clone() const final + { + return std::make_unique(m_value); + } + + // Get the type at runtime. + [[nodiscard]] std::type_index get_type_index() const final + { + return std::type_index{typeid(T)}; + } + + // Raw getters for the internal instance. + [[nodiscard]] const void *get_ptr() const final + { + return &m_value; } - } - [[nodiscard]] std::string get_extra_info() const final { - if constexpr (udpla_has_get_name_v) { - return m_value.get_extra_info(); - } else { - return ""; + void *get_ptr() final + { + return &m_value; } - } - [[nodiscard]] double get_mu_central_body() const final { - if constexpr (udpla_has_get_mu_central_body_v) { - return m_value.get_mu_central_body(); - } else { - return -1.; // by convention, in kep3 this signals the planet does not - // expose this physical value + + // Mandatory methods. + [[nodiscard]] std::array, 2> eph(const epoch &ep) const final + { + return m_value.eph(ep); + } + // optional methods with default implementations + // these require added boiler plate as to detect whether they have been + // implemented by the user class. + [[nodiscard]] std::string get_name() const final + { + if constexpr (udpla_has_get_name_v) { + return m_value.get_name(); + } else { + return detail::type_name(); + } + } + [[nodiscard]] std::string get_extra_info() const final + { + if constexpr (udpla_has_get_name_v) { + return m_value.get_extra_info(); + } else { + return ""; + } } - } - [[nodiscard]] double get_mu_self() const final { - if constexpr (udpla_has_get_mu_self_v) { - return m_value.get_mu_self(); - } else { - return -1.; // by convention, in kep3 this signals the planet does not - // expose this physical value + [[nodiscard]] double get_mu_central_body() const final + { + if constexpr (udpla_has_get_mu_central_body_v) { + return m_value.get_mu_central_body(); + } else { + return -1.; // by convention, in kep3 this signals the planet does not + // expose this physical value + } } - } - [[nodiscard]] double get_radius() const final { - if constexpr (udpla_has_get_radius_v) { - return m_value.get_radius(); - } else { - return -1.; // by convention, in kep3 this signals the planet does not - // expose this physical value + [[nodiscard]] double get_mu_self() const final + { + if constexpr (udpla_has_get_mu_self_v) { + return m_value.get_mu_self(); + } else { + return -1.; // by convention, in kep3 this signals the planet does not + // expose this physical value + } } - } - [[nodiscard]] double get_safe_radius() const final { - if constexpr (udpla_has_get_safe_radius_v) { - return m_value.get_safe_radius(); - } else { - return -1.; // by convention, in kep3 this signals the planet does not - // expose this physical value + [[nodiscard]] double get_radius() const final + { + if constexpr (udpla_has_get_radius_v) { + return m_value.get_radius(); + } else { + return -1.; // by convention, in kep3 this signals the planet does not + // expose this physical value + } } - } - - [[nodiscard]] double period(const kep3::epoch &ep) const final { - // If the user provides an efficient way to compute the period, then use it - if constexpr (udpla_has_period_v) { - return m_value.period(ep); - } else if constexpr (udpla_has_get_mu_central_body_v) { - // If the user provides the central body parameter, then compute the - // period from the energy at epoch - auto [r, v] = eph(ep); - double mu = get_mu_central_body(); - double R = std::sqrt(r[0] * r[0] + r[1] * r[1] + r[2] * r[2]); - double v2 = v[0] * v[0] + v[1] * v[1] + v[2] * v[2]; - double en = v2 / 2. - mu / R; - if (en > 0) { - // If the energy is positive we have an hyperbolae and we return nan - return std::numeric_limits::quiet_NaN(); - } else { - double a = -mu / 2. / en; - return kep3::pi * 2. * std::sqrt(a * a * a / mu); - } - } else { - // There is no way to compute a period for this planet - throw not_implemented_error( - "A period nor a central body mu has been declared for '" + - get_name() + "', impossible to provide a default implementation"); + [[nodiscard]] double get_safe_radius() const final + { + if constexpr (udpla_has_get_safe_radius_v) { + return m_value.get_safe_radius(); + } else { + return -1.; // by convention, in kep3 this signals the planet does not + // expose this physical value + } + } + + [[nodiscard]] double period(const kep3::epoch &ep) const final + { + // If the user provides an efficient way to compute the period, then use it + if constexpr (udpla_has_period_v) { + return m_value.period(ep); + } else if constexpr (udpla_has_get_mu_central_body_v) { + // If the user provides the central body parameter, then compute the + // period from the energy at epoch + auto [r, v] = eph(ep); + double mu = get_mu_central_body(); + double R = std::sqrt(r[0] * r[0] + r[1] * r[1] + r[2] * r[2]); + double v2 = v[0] * v[0] + v[1] * v[1] + v[2] * v[2]; + double en = v2 / 2. - mu / R; + if (en > 0) { + // If the energy is positive we have an hyperbolae and we return nan + return std::numeric_limits::quiet_NaN(); + } else { + double a = -mu / 2. / en; + return kep3::pi * 2. * std::sqrt(a * a * a / mu); + } + } else { + // There is no way to compute a period for this planet + throw not_implemented_error("A period nor a central body mu has been declared for '" + get_name() + + "', impossible to provide a default implementation"); + } } - } private: - // Serialization. - friend class boost::serialization::access; - template void serialize(Archive &ar, unsigned) { - ar &boost::serialization::base_object(*this); - ar &m_value; - } + // Serialization. + friend class boost::serialization::access; + template + void serialize(Archive &ar, unsigned) + { + ar &boost::serialization::base_object(*this); + ar & m_value; + } }; template -using is_udpla = std::conjunction< - std::is_same>, std::is_default_constructible, - std::is_copy_constructible, std::is_move_constructible, - std::is_destructible, udpla_has_eph>; +using is_udpla = std::conjunction>, std::is_default_constructible, + std::is_copy_constructible, std::is_move_constructible, std::is_destructible, + udpla_has_eph>; struct kep3_DLL_PUBLIC null_udpla { - null_udpla() = default; - static std::array, 2> eph(const epoch &); + null_udpla() = default; + static std::array, 2> eph(const epoch &); private: - friend class boost::serialization::access; - template void serialize(Archive &, unsigned){}; + friend class boost::serialization::access; + template + void serialize(Archive &, unsigned){}; }; } // namespace kep3::detail kep3_S11N_PLANET_EXPORT_KEY(kep3::detail::null_udpla); // Disable Boost.Serialization tracking for the implementation // details of the planet. -BOOST_CLASS_TRACKING(kep3::detail::planet_inner_base, - boost::serialization::track_never) +BOOST_CLASS_TRACKING(kep3::detail::planet_inner_base, boost::serialization::track_never) -namespace kep3 { +namespace kep3 +{ // The final class -class kep3_DLL_PUBLIC planet { - // Pointer to the inner base. - std::unique_ptr m_ptr; - - // Serialization. - friend class boost::serialization::access; - template void serialize(Archive &ar, unsigned) { - ar &m_ptr; - } - - // Just two small helpers to make sure via assertions that whenever we require - // access to the pointer it actually points to something. - [[nodiscard]] const detail::planet_inner_base *ptr() const; - detail::planet_inner_base *ptr(); - - template - using generic_ctor_enabler = std::enable_if_t< - std::conjunction_v< - std::negation>>, - detail::is_udpla>>, - int>; +class kep3_DLL_PUBLIC planet +{ + // Pointer to the inner base. + std::unique_ptr m_ptr; + + // Serialization. + friend class boost::serialization::access; + template + void serialize(Archive &ar, unsigned) + { + ar & m_ptr; + } + + // Just two small helpers to make sure via assertions that whenever we require + // access to the pointer it actually points to something. + [[nodiscard]] const detail::planet_inner_base *ptr() const; + detail::planet_inner_base *ptr(); + + template + using generic_ctor_enabler + = std::enable_if_t>>, + detail::is_udpla>>, + int>; public: - // Default constructor - planet(); - // Constructor from the UDPLA - template = 0> - explicit planet(T &&x) - : m_ptr(std::make_unique>>( - std::forward(x))) {} - // Copy constructor - planet(const planet &); - // Move ctor. - planet(planet &&) noexcept; - // Move assignment. - planet &operator=(planet &&) noexcept; - // Copy assignment. - planet &operator=(const planet &); - // Default destructor - ~planet() = default; - // Assignment from a user-defined planet of type \p T - template = 0> planet &operator=(T &&x) { - return (*this) = planet(std::forward(x)); - } - // Extract a const pointer to the UDPLA. - template const T *extract() const noexcept { + // Default constructor + planet(); + // Constructor from the UDPLA + template = 0> + explicit planet(T &&x) : m_ptr(std::make_unique>>(std::forward(x))) + { + } + // Copy constructor + planet(const planet &); + // Move ctor. + planet(planet &&) noexcept; + // Move assignment. + planet &operator=(planet &&) noexcept; + // Copy assignment. + planet &operator=(const planet &); + // Default destructor + ~planet() = default; + // Assignment from a user-defined planet of type \p T + template = 0> + planet &operator=(T &&x) + { + return (*this) = planet(std::forward(x)); + } + // Extract a const pointer to the UDPLA. + template + const T *extract() const noexcept + { #if defined(kep3_PREFER_TYPEID_NAME_EXTRACT) - return detail::typeid_name_extract(*this); + return detail::typeid_name_extract(*this); #else - auto p = dynamic_cast *>(ptr()); - return p == nullptr ? nullptr : &(p->m_value); + auto p = dynamic_cast *>(ptr()); + return p == nullptr ? nullptr : &(p->m_value); #endif - } - /// Extract a pointer to the UDA. - template T *extract() noexcept { + } + /// Extract a pointer to the UDA. + template + T *extract() noexcept + { #if defined(kep3_PREFER_TYPEID_NAME_EXTRACT) - return detail::typeid_name_extract(*this); + return detail::typeid_name_extract(*this); #else - auto p = dynamic_cast *>(ptr()); - return p == nullptr ? nullptr : &(p->m_value); + auto p = dynamic_cast *>(ptr()); + return p == nullptr ? nullptr : &(p->m_value); #endif - } - // Checks the user-defined algorithm type at run-time. - template [[nodiscard]] bool is() const noexcept { - return extract() != nullptr; - } - // Check if the planet is valid (i.e. has not been moved from) - [[nodiscard]] bool is_valid() const; - // Gets the type of the UDPLA at runtime. - [[nodiscard]] std::type_index get_type_index() const; - /// Gets a const pointer to the UDPLA. - [[nodiscard]] const void *get_ptr() const; - // Gets a mutable pointer to the UDPLA. - void *get_ptr(); - - std::array, 2> eph(const epoch &); - [[nodiscard]] std::string get_name() const; - [[nodiscard]] std::string get_extra_info() const; - [[nodiscard]] double get_mu_central_body() const; - [[nodiscard]] double get_mu_self() const; - [[nodiscard]] double get_radius() const; - [[nodiscard]] double get_safe_radius() const; - [[nodiscard]] double period(const kep3::epoch & = kep3::epoch()) const; + } + // Checks the user-defined algorithm type at run-time. + template + [[nodiscard]] bool is() const noexcept + { + return extract() != nullptr; + } + // Check if the planet is valid (i.e. has not been moved from) + [[nodiscard]] bool is_valid() const; + // Gets the type of the UDPLA at runtime. + [[nodiscard]] std::type_index get_type_index() const; + /// Gets a const pointer to the UDPLA. + [[nodiscard]] const void *get_ptr() const; + // Gets a mutable pointer to the UDPLA. + void *get_ptr(); + + std::array, 2> eph(const epoch &); + [[nodiscard]] std::string get_name() const; + [[nodiscard]] std::string get_extra_info() const; + [[nodiscard]] double get_mu_central_body() const; + [[nodiscard]] double get_mu_self() const; + [[nodiscard]] double get_radius() const; + [[nodiscard]] double get_safe_radius() const; + [[nodiscard]] double period(const kep3::epoch & = kep3::epoch()) const; }; // Streaming operator for algorithm. diff --git a/include/kep3/planets/jpl_lp.hpp b/include/kep3/planets/jpl_lp.hpp index dcb95e73..bf7377ca 100644 --- a/include/kep3/planets/jpl_lp.hpp +++ b/include/kep3/planets/jpl_lp.hpp @@ -20,7 +20,8 @@ #include #include -namespace kep3::udpla { +namespace kep3::udpla +{ /// Solar System Planet (jpl simplified ephemerides) /** @@ -31,56 +32,58 @@ namespace kep3::udpla { * 1800AD - 2050 AD */ -class kep3_DLL_PUBLIC jpl_lp { +class kep3_DLL_PUBLIC jpl_lp +{ - std::array m_elements; - std::array m_elements_dot; - std::string m_name; - double m_mu_central_body; - double m_mu_self; - double m_radius; - double m_safe_radius; + std::array m_elements; + std::array m_elements_dot; + std::string m_name; + double m_mu_central_body; + double m_mu_self; + double m_radius; + double m_safe_radius; - friend class boost::serialization::access; - template void serialize(Archive &ar, unsigned) { - ar &m_elements; - ar &m_elements_dot; - ar &m_name; - ar &m_mu_central_body; - ar &m_mu_self; - ar &m_radius; - ar &m_safe_radius; - } + friend class boost::serialization::access; + template + void serialize(Archive &ar, unsigned) + { + ar & m_elements; + ar & m_elements_dot; + ar & m_name; + ar & m_mu_central_body; + ar & m_mu_self; + ar & m_radius; + ar & m_safe_radius; + } public: - // Constructor - explicit jpl_lp(std::string = "earth"); - // Mandatory UDPLA methods - [[nodiscard]] std::array, 2> eph(const epoch &) const; + // Constructor + explicit jpl_lp(std::string = "earth"); + // Mandatory UDPLA methods + [[nodiscard]] std::array, 2> eph(const epoch &) const; - // Optional UDPLA methods - [[nodiscard]] std::string get_name() const; - [[nodiscard]] double get_mu_central_body() const; - [[nodiscard]] double get_mu_self() const; - [[nodiscard]] double get_radius() const; - [[nodiscard]] double get_safe_radius() const; - [[nodiscard]] std::string get_extra_info() const; + // Optional UDPLA methods + [[nodiscard]] std::string get_name() const; + [[nodiscard]] double get_mu_central_body() const; + [[nodiscard]] double get_mu_self() const; + [[nodiscard]] double get_radius() const; + [[nodiscard]] double get_safe_radius() const; + [[nodiscard]] std::string get_extra_info() const; - // Other methods - [[nodiscard]] std::array - elements(const kep3::epoch & = kep3::epoch(), - kep3::elements_type = kep3::elements_type::KEP_F) const; + // Other methods + [[nodiscard]] std::array elements(const kep3::epoch & = kep3::epoch(), + kep3::elements_type = kep3::elements_type::KEP_F) const; private: - [[nodiscard]] std::array - _f_elements(const kep3::epoch & = kep3::epoch()) const; + [[nodiscard]] std::array _f_elements(const kep3::epoch & = kep3::epoch()) const; }; -kep3_DLL_PUBLIC std::ostream &operator<<(std::ostream &, - const kep3::udpla::jpl_lp &); +kep3_DLL_PUBLIC std::ostream &operator<<(std::ostream &, const kep3::udpla::jpl_lp &); } // namespace kep3::udpla // fmt formatter redirecting to the stream operator -template <> struct fmt::formatter : ostream_formatter {}; +template <> +struct fmt::formatter : ostream_formatter { +}; // necessary for serialization kep3_S11N_PLANET_EXPORT_KEY(kep3::udpla::jpl_lp); diff --git a/include/kep3/planets/keplerian.hpp b/include/kep3/planets/keplerian.hpp index 0025e4f3..aaf667fc 100644 --- a/include/kep3/planets/keplerian.hpp +++ b/include/kep3/planets/keplerian.hpp @@ -20,71 +20,74 @@ #include #include -namespace kep3::udpla { +namespace kep3::udpla +{ -class kep3_DLL_PUBLIC keplerian { +class kep3_DLL_PUBLIC keplerian +{ - kep3::epoch m_ref_epoch; - std::string m_name; - double m_mu_central_body; - double m_mu_self; - double m_radius; - double m_safe_radius; - double m_period; - bool m_ellipse; - std::array, 2> m_pos_vel_0; + kep3::epoch m_ref_epoch; + std::string m_name; + double m_mu_central_body; + double m_mu_self; + double m_radius; + double m_safe_radius; + double m_period; + bool m_ellipse; + std::array, 2> m_pos_vel_0; - friend class boost::serialization::access; - template void serialize(Archive &ar, unsigned) { - ar &m_ref_epoch; - ar &m_name; - ar &m_mu_central_body; - ar &m_mu_self; - ar &m_radius; - ar &m_safe_radius; - ar &m_period; - ar &m_ellipse; - ar &m_safe_radius; - ar &m_pos_vel_0; - } + friend class boost::serialization::access; + template + void serialize(Archive &ar, unsigned) + { + ar & m_ref_epoch; + ar & m_name; + ar & m_mu_central_body; + ar & m_mu_self; + ar & m_radius; + ar & m_safe_radius; + ar & m_period; + ar & m_ellipse; + ar & m_safe_radius; + ar & m_pos_vel_0; + } public: - // NOTE: added_param contains mu_self, radius and safe_radius - explicit keplerian(const epoch &ref_epoch, const std::array &par, - double mu_central_body = 1., std::string name = "Unknown", + // NOTE: added_param contains mu_self, radius and safe_radius + explicit keplerian(const epoch &ref_epoch, const std::array &par, double mu_central_body = 1., + std::string name = "Unknown", - std::array added_params = {-1., -1., -1.}, kep3::elements_type el_t = kep3::elements_type::KEP_F); - // Constructor from pos_vel - explicit keplerian(const epoch &ref_epoch = kep3::epoch(0), + std::array added_params = {-1., -1., -1.}, + kep3::elements_type el_t = kep3::elements_type::KEP_F); + // Constructor from pos_vel + explicit keplerian(const epoch &ref_epoch = kep3::epoch(0), - const std::array, 2> &pos_vel = - {{{1.0, 0.0, 0.0}, {0., 1.0, 0.0}}}, - double mu_central_body = 1., std::string name = "Unknown", - std::array added_params = {-1., -1., -1.}); - // Mandatory UDPLA methods - [[nodiscard]] std::array, 2> eph(const epoch &) const; + const std::array, 2> &pos_vel = {{{1.0, 0.0, 0.0}, {0., 1.0, 0.0}}}, + double mu_central_body = 1., std::string name = "Unknown", + std::array added_params = {-1., -1., -1.}); + // Mandatory UDPLA methods + [[nodiscard]] std::array, 2> eph(const epoch &) const; - // Optional UDPLA methods - [[nodiscard]] std::string get_name() const; - [[nodiscard]] double get_mu_central_body() const; - [[nodiscard]] double get_mu_self() const; - [[nodiscard]] double get_radius() const; - [[nodiscard]] double get_safe_radius() const; - [[nodiscard]] std::string get_extra_info() const; - [[nodiscard]] double period(const kep3::epoch & = kep3::epoch()) const; + // Optional UDPLA methods + [[nodiscard]] std::string get_name() const; + [[nodiscard]] double get_mu_central_body() const; + [[nodiscard]] double get_mu_self() const; + [[nodiscard]] double get_radius() const; + [[nodiscard]] double get_safe_radius() const; + [[nodiscard]] std::string get_extra_info() const; + [[nodiscard]] double period(const kep3::epoch & = kep3::epoch()) const; - // Other methods - [[nodiscard]] kep3::epoch get_ref_epoch() const; - [[nodiscard]] std::array - elements(kep3::elements_type = kep3::elements_type::KEP_F) const; + // Other methods + [[nodiscard]] kep3::epoch get_ref_epoch() const; + [[nodiscard]] std::array elements(kep3::elements_type = kep3::elements_type::KEP_F) const; }; -kep3_DLL_PUBLIC std::ostream &operator<<(std::ostream &, - const kep3::udpla::keplerian &); +kep3_DLL_PUBLIC std::ostream &operator<<(std::ostream &, const kep3::udpla::keplerian &); } // namespace kep3::udpla // fmt formatter redirecting to the stream operator template <> -struct fmt::formatter : ostream_formatter {}; +struct fmt::formatter : ostream_formatter { +}; // necessary for serialization kep3_S11N_PLANET_EXPORT_KEY(kep3::udpla::keplerian); diff --git a/pykep/core.cpp b/pykep/core.cpp index ddff99df..7964cf30 100644 --- a/pykep/core.cpp +++ b/pykep/core.cpp @@ -25,68 +25,66 @@ namespace py = pybind11; namespace pk = pykep; -PYBIND11_MODULE(core, m) { - py::options options; - options.disable_function_signatures(); - m.doc() = pk::core_module_doc(); +PYBIND11_MODULE(core, m) +{ + py::options options; + options.disable_function_signatures(); + m.doc() = pk::core_module_doc(); - // We expose various global constants: - m.attr("AU") = py::float_(kep3::AU); - m.attr("CAVENDISH") = py::float_(kep3::CAVENDISH); - m.attr("MU_SUN") = py::float_(kep3::MU_SUN); - m.attr("MU_EARTH") = py::float_(kep3::MU_EARTH); - m.attr("EARTH_VELOCITY") = py::float_(kep3::EARTH_VELOCITY); - m.attr("EARTH_J2") = py::float_(kep3::EARTH_J2); - m.attr("EARTH_RADIUS") = py::float_(kep3::EARTH_RADIUS); - m.attr("RAD2DEG") = py::float_(kep3::RAD2DEG); - m.attr("DAY2SEC") = py::float_(kep3::DAY2SEC); - m.attr("SEC2DAY") = py::float_(kep3::SEC2DAY); - m.attr("DAY2YEAR") = py::float_(kep3::DAY2YEAR); - m.attr("G0") = py::float_(kep3::G0); + // We expose various global constants: + m.attr("AU") = py::float_(kep3::AU); + m.attr("CAVENDISH") = py::float_(kep3::CAVENDISH); + m.attr("MU_SUN") = py::float_(kep3::MU_SUN); + m.attr("MU_EARTH") = py::float_(kep3::MU_EARTH); + m.attr("EARTH_VELOCITY") = py::float_(kep3::EARTH_VELOCITY); + m.attr("EARTH_J2") = py::float_(kep3::EARTH_J2); + m.attr("EARTH_RADIUS") = py::float_(kep3::EARTH_RADIUS); + m.attr("RAD2DEG") = py::float_(kep3::RAD2DEG); + m.attr("DAY2SEC") = py::float_(kep3::DAY2SEC); + m.attr("SEC2DAY") = py::float_(kep3::SEC2DAY); + m.attr("DAY2YEAR") = py::float_(kep3::DAY2YEAR); + m.attr("G0") = py::float_(kep3::G0); - // We expose here global enums: - py::enum_(m, "elements_type", "") - .value("KEP_M", kep3::KEP_M, - "Keplerian Elements a,e,i,W,w,M (Mean anomaly)") - .value("KEP_F", kep3::KEP_F, - "Keplerian Elements a,e,i,W,w,f (True anomaly)") - .value("MEQ", kep3::MEQ, - "Modified Equinoctial Elements p,f,g,h,k,L (Mean Longitude)") - .value("MEQ_R", kep3::MEQ_R, - "Modified Equinoctial Elements (retrograde) p,f,g,h,k,L (Mean " - "Longitude)") - .value("POSVEL", kep3::POSVEL, "Position and Velocity") - .export_values(); + // We expose here global enums: + py::enum_(m, "elements_type", "") + .value("KEP_M", kep3::KEP_M, "Keplerian Elements a,e,i,W,w,M (Mean anomaly)") + .value("KEP_F", kep3::KEP_F, "Keplerian Elements a,e,i,W,w,f (True anomaly)") + .value("MEQ", kep3::MEQ, "Modified Equinoctial Elements p,f,g,h,k,L (Mean Longitude)") + .value("MEQ_R", kep3::MEQ_R, + "Modified Equinoctial Elements (retrograde) p,f,g,h,k,L (Mean " + "Longitude)") + .value("POSVEL", kep3::POSVEL, "Position and Velocity") + .export_values(); - // We expose the various anomaly conversions - m.def("m2e", &kep3::m2e, pk::m2e_doc().c_str()); - m.def("e2m", &kep3::e2m, pk::e2m_doc().c_str()); - m.def("m2f", &kep3::m2f, pk::m2f_doc().c_str()); - m.def("f2m", &kep3::f2m, pk::f2m_doc().c_str()); - m.def("e2f", &kep3::e2f, pk::e2f_doc().c_str()); - m.def("f2e", &kep3::f2e, pk::f2e_doc().c_str()); - m.def("n2h", &kep3::n2h, pk::n2h_doc().c_str()); - m.def("h2n", &kep3::h2n, pk::h2n_doc().c_str()); - m.def("n2f", &kep3::n2f, pk::n2f_doc().c_str()); - m.def("f2n", &kep3::f2n, pk::f2n_doc().c_str()); - m.def("h2f", &kep3::h2f, pk::h2f_doc().c_str()); - m.def("f2h", &kep3::f2h, pk::f2h_doc().c_str()); - m.def("zeta2f", &kep3::zeta2f, pk::zeta2f_doc().c_str()); - m.def("f2zeta", &kep3::f2zeta, pk::f2zeta_doc().c_str()); + // We expose the various anomaly conversions + m.def("m2e", &kep3::m2e, pk::m2e_doc().c_str()); + m.def("e2m", &kep3::e2m, pk::e2m_doc().c_str()); + m.def("m2f", &kep3::m2f, pk::m2f_doc().c_str()); + m.def("f2m", &kep3::f2m, pk::f2m_doc().c_str()); + m.def("e2f", &kep3::e2f, pk::e2f_doc().c_str()); + m.def("f2e", &kep3::f2e, pk::f2e_doc().c_str()); + m.def("n2h", &kep3::n2h, pk::n2h_doc().c_str()); + m.def("h2n", &kep3::h2n, pk::h2n_doc().c_str()); + m.def("n2f", &kep3::n2f, pk::n2f_doc().c_str()); + m.def("f2n", &kep3::f2n, pk::f2n_doc().c_str()); + m.def("h2f", &kep3::h2f, pk::h2f_doc().c_str()); + m.def("f2h", &kep3::f2h, pk::f2h_doc().c_str()); + m.def("zeta2f", &kep3::zeta2f, pk::zeta2f_doc().c_str()); + m.def("f2zeta", &kep3::f2zeta, pk::f2zeta_doc().c_str()); - // And their vectorized versions - m.def("m2e_v", py::vectorize(kep3::m2e), pk::m2e_v_doc().c_str()); - m.def("e2m_v", py::vectorize(kep3::e2m), pk::e2m_v_doc().c_str()); - m.def("m2f_v", py::vectorize(kep3::m2f), pk::m2f_v_doc().c_str()); - m.def("f2m_v", py::vectorize(kep3::f2m), pk::f2m_v_doc().c_str()); - m.def("e2f_v", py::vectorize(kep3::e2f), pk::e2f_v_doc().c_str()); - m.def("f2e_v", py::vectorize(kep3::f2e), pk::f2e_v_doc().c_str()); - m.def("n2h_v", py::vectorize(kep3::n2h), pk::n2h_v_doc().c_str()); - m.def("h2n_v", py::vectorize(kep3::h2n), pk::h2n_v_doc().c_str()); - m.def("n2f_v", py::vectorize(kep3::n2f), pk::n2f_v_doc().c_str()); - m.def("f2n_v", py::vectorize(kep3::f2n), pk::f2n_v_doc().c_str()); - m.def("h2f_v", py::vectorize(kep3::h2f), pk::h2f_v_doc().c_str()); - m.def("f2h_v", py::vectorize(kep3::f2h), pk::f2h_v_doc().c_str()); - m.def("zeta2f_v", py::vectorize(kep3::zeta2f), pk::zeta2f_v_doc().c_str()); - m.def("f2zeta_v", py::vectorize(kep3::f2zeta), pk::f2zeta_v_doc().c_str()); + // And their vectorized versions + m.def("m2e_v", py::vectorize(kep3::m2e), pk::m2e_v_doc().c_str()); + m.def("e2m_v", py::vectorize(kep3::e2m), pk::e2m_v_doc().c_str()); + m.def("m2f_v", py::vectorize(kep3::m2f), pk::m2f_v_doc().c_str()); + m.def("f2m_v", py::vectorize(kep3::f2m), pk::f2m_v_doc().c_str()); + m.def("e2f_v", py::vectorize(kep3::e2f), pk::e2f_v_doc().c_str()); + m.def("f2e_v", py::vectorize(kep3::f2e), pk::f2e_v_doc().c_str()); + m.def("n2h_v", py::vectorize(kep3::n2h), pk::n2h_v_doc().c_str()); + m.def("h2n_v", py::vectorize(kep3::h2n), pk::h2n_v_doc().c_str()); + m.def("n2f_v", py::vectorize(kep3::n2f), pk::n2f_v_doc().c_str()); + m.def("f2n_v", py::vectorize(kep3::f2n), pk::f2n_v_doc().c_str()); + m.def("h2f_v", py::vectorize(kep3::h2f), pk::h2f_v_doc().c_str()); + m.def("f2h_v", py::vectorize(kep3::f2h), pk::f2h_v_doc().c_str()); + m.def("zeta2f_v", py::vectorize(kep3::zeta2f), pk::zeta2f_v_doc().c_str()); + m.def("f2zeta_v", py::vectorize(kep3::f2zeta), pk::f2zeta_v_doc().c_str()); } \ No newline at end of file diff --git a/pykep/docstrings.cpp b/pykep/docstrings.cpp index 0abc3e86..b6ab045e 100644 --- a/pykep/docstrings.cpp +++ b/pykep/docstrings.cpp @@ -11,15 +11,18 @@ #include "docstrings.hpp" -namespace pykep { +namespace pykep +{ -std::string core_module_doc() { - return R"(core is the Pykep module that contains most of its core routines efficiently coded in c++ +std::string core_module_doc() +{ + return R"(core is the Pykep module that contains most of its core routines efficiently coded in c++ )"; } -std::string m2e_doc() { - return R"(m2e(M, ecc) +std::string m2e_doc() +{ + return R"(m2e(M, ecc) Converts from Mean to Eccentric anomaly. Requires ecc < 1. @@ -40,8 +43,9 @@ std::string m2e_doc() { )"; } -std::string e2m_doc() { - return R"(e2m(E, ecc) +std::string e2m_doc() +{ + return R"(e2m(E, ecc) Converts from Eccentric to Mean anomaly. Requires ecc < 1. @@ -62,8 +66,9 @@ std::string e2m_doc() { )"; } -std::string e2f_doc() { - return R"(e2f(E, ecc) +std::string e2f_doc() +{ + return R"(e2f(E, ecc) Converts from eccentric to true anomaly. Requires ecc < 1. @@ -84,8 +89,9 @@ std::string e2f_doc() { )"; } -std::string f2e_doc() { - return R"(f2e(f, ecc) +std::string f2e_doc() +{ + return R"(f2e(f, ecc) Converts from True to Eccentric anomaly. Requires ecc < 1. @@ -106,8 +112,9 @@ std::string f2e_doc() { )"; } -std::string f2m_doc() { - return R"(f2m(f, ecc) +std::string f2m_doc() +{ + return R"(f2m(f, ecc) Converts from True to Mean anomaly. Requires ecc < 1. @@ -128,8 +135,9 @@ std::string f2m_doc() { )"; } -std::string m2f_doc() { - return R"(m2f(M, ecc) +std::string m2f_doc() +{ + return R"(m2f(M, ecc) Converts from Mean to True anomaly. Requires ecc < 1. @@ -150,8 +158,9 @@ std::string m2f_doc() { )"; } -std::string h2n_doc() { - return R"(h2n(H, ecc) +std::string h2n_doc() +{ + return R"(h2n(H, ecc) Converts from Hyperbolic to Hyperbolic Mean anomaly. Requires ecc > 1. @@ -172,8 +181,9 @@ std::string h2n_doc() { )"; } -std::string n2h_doc() { - return R"(n2h(N, ecc) +std::string n2h_doc() +{ + return R"(n2h(N, ecc) Converts from Hyperbolic Mean to Hyperbolic anomaly. Requires ecc > 1. @@ -194,8 +204,9 @@ std::string n2h_doc() { )"; } -std::string h2f_doc() { - return R"(h2f(H, ecc) +std::string h2f_doc() +{ + return R"(h2f(H, ecc) Converts from Hyperbolic to True anomaly. Requires ecc > 1. @@ -216,8 +227,9 @@ std::string h2f_doc() { )"; } -std::string f2h_doc() { - return R"(f2h(f, ecc) +std::string f2h_doc() +{ + return R"(f2h(f, ecc) Converts from True to Hyperbolic anomaly. Requires ecc > 1. @@ -238,8 +250,9 @@ std::string f2h_doc() { )"; } -std::string f2n_doc() { - return R"(f2n(f, ecc) +std::string f2n_doc() +{ + return R"(f2n(f, ecc) Converts from True to Hyperbolic Mean anomaly. Requires ecc > 1. @@ -260,8 +273,9 @@ std::string f2n_doc() { )"; } -std::string n2f_doc() { - return R"(n2f(N, ecc) +std::string n2f_doc() +{ + return R"(n2f(N, ecc) Converts from Hyperbolic Mean to True anomaly. Requires ecc > 1. @@ -282,8 +296,9 @@ std::string n2f_doc() { )"; } -std::string zeta2f_doc() { - return R"(zeta2f(zeta, ecc) +std::string zeta2f_doc() +{ + return R"(zeta2f(zeta, ecc) Converts from Gudermannian to True anomaly. Requires ecc > 1. @@ -307,8 +322,9 @@ std::string zeta2f_doc() { )"; } -std::string f2zeta_doc() { - return R"(f2zeta(f, ecc) +std::string f2zeta_doc() +{ + return R"(f2zeta(f, ecc) Converts from True anomaly to Gudermannian. Requires ecc > 1. @@ -329,8 +345,9 @@ std::string f2zeta_doc() { )"; } -std::string m2e_v_doc() { - return R"(m2e_v(Ms, eccs) +std::string m2e_v_doc() +{ + return R"(m2e_v(Ms, eccs) Converts from Mean to Eccentric anomaly (vectorized version). Requires ecc < 1. @@ -353,8 +370,9 @@ std::string m2e_v_doc() { )"; } -std::string e2m_v_doc() { - return R"(e2m_v(Es, eccs) +std::string e2m_v_doc() +{ + return R"(e2m_v(Es, eccs) Converts from Eccentric to Mean anomaly (vectorized version). Requires ecc < 1. @@ -377,8 +395,9 @@ std::string e2m_v_doc() { )"; } -std::string e2f_v_doc() { - return R"(e2f_v(Es, eccs) +std::string e2f_v_doc() +{ + return R"(e2f_v(Es, eccs) Converts from eccentric to true anomaly (vectorized version). Requires ecc < 1. @@ -401,8 +420,9 @@ std::string e2f_v_doc() { )"; } -std::string f2e_v_doc() { - return R"(f2e_v(fs, eccs) +std::string f2e_v_doc() +{ + return R"(f2e_v(fs, eccs) Converts from True to Eccentric anomaly (vectorized version). Requires ecc < 1. @@ -425,8 +445,9 @@ std::string f2e_v_doc() { )"; } -std::string f2m_v_doc() { - return R"(f2m_v(fs, eccs) +std::string f2m_v_doc() +{ + return R"(f2m_v(fs, eccs) Converts from True to Mean anomaly (vectorized version). Requires ecc < 1. @@ -449,8 +470,9 @@ std::string f2m_v_doc() { )"; } -std::string m2f_v_doc() { - return R"(m2f_v(Ms, eccs) +std::string m2f_v_doc() +{ + return R"(m2f_v(Ms, eccs) Converts from Mean to True anomaly (vectorized version). Requires ecc < 1. @@ -473,8 +495,9 @@ std::string m2f_v_doc() { )"; } -std::string h2n_v_doc() { - return R"(h2n_v(Hs, eccs) +std::string h2n_v_doc() +{ + return R"(h2n_v(Hs, eccs) Converts from Hyperbolic to Hyperbolic Mean anomaly (vectorized version). Requires ecc > 1. @@ -497,8 +520,9 @@ std::string h2n_v_doc() { )"; } -std::string n2h_v_doc() { - return R"(n2h_v(Ns, eccs) +std::string n2h_v_doc() +{ + return R"(n2h_v(Ns, eccs) Converts from Hyperbolic Mean to Hyperbolic anomaly (vectorized version). Requires ecc > 1. @@ -521,8 +545,9 @@ std::string n2h_v_doc() { )"; } -std::string h2f_v_doc() { - return R"(h2f_v(Hs, eccs) +std::string h2f_v_doc() +{ + return R"(h2f_v(Hs, eccs) Converts from Hyperbolic to True anomaly (vectorized version). Requires ecc > 1. @@ -545,8 +570,9 @@ std::string h2f_v_doc() { )"; } -std::string f2h_v_doc() { - return R"(f2h_v(fs, eccs) +std::string f2h_v_doc() +{ + return R"(f2h_v(fs, eccs) Converts from True to Hyperbolic anomaly (vectorized version). Requires ecc > 1. @@ -569,8 +595,9 @@ std::string f2h_v_doc() { )"; } -std::string f2n_v_doc() { - return R"(f2n_v(fs, eccs) +std::string f2n_v_doc() +{ + return R"(f2n_v(fs, eccs) Converts from True to Hyperbolic Mean anomaly (vectorized version). Requires ecc > 1. @@ -593,8 +620,9 @@ std::string f2n_v_doc() { )"; } -std::string n2f_v_doc() { - return R"(n2f_v(Ns, eccs) +std::string n2f_v_doc() +{ + return R"(n2f_v(Ns, eccs) Converts from Hyperbolic Mean to True anomaly (vectorized version). Requires ecc > 1. @@ -617,8 +645,9 @@ std::string n2f_v_doc() { )"; } -std::string zeta2f_v_doc() { - return R"(zeta2f_v(zetas, eccs) +std::string zeta2f_v_doc() +{ + return R"(zeta2f_v(zetas, eccs) Converts from Gudermannian to True anomaly (vectorized version). Requires ecc > 1. @@ -644,8 +673,9 @@ std::string zeta2f_v_doc() { )"; } -std::string f2zeta_v_doc() { - return R"(f2zeta_v(fs, eccs) +std::string f2zeta_v_doc() +{ + return R"(f2zeta_v(fs, eccs) Converts from True anomaly to Gudermannian (vectorized version). Requires ecc > 1. diff --git a/pykep/docstrings.hpp b/pykep/docstrings.hpp index fcaa1f95..8026a37d 100644 --- a/pykep/docstrings.hpp +++ b/pykep/docstrings.hpp @@ -12,7 +12,8 @@ #include -namespace pykep { +namespace pykep +{ // Modules std::string core_module_doc(); diff --git a/src/core_astro/eq2par2eq.cpp b/src/core_astro/eq2par2eq.cpp index b1d68ca3..6892478c 100644 --- a/src/core_astro/eq2par2eq.cpp +++ b/src/core_astro/eq2par2eq.cpp @@ -13,54 +13,57 @@ #include #include -namespace kep3 { +namespace kep3 +{ -std::array eq2par(const std::array &eq, bool retrogade) { - std::array retval{}; - int I = 1; - if (retrogade) { - I = -1; - } - double ecc = std::sqrt(eq[1] * eq[1] + eq[2] * eq[2]); - double tmp = std::sqrt(eq[3] * eq[3] + eq[4] * eq[4]); - double zita = std::atan2(eq[2] / ecc, eq[1] / ecc); // [-pi, pi] - if (zita < 0) { - zita += 2 * pi; // [0, 2*pi] - } +std::array eq2par(const std::array &eq, bool retrogade) +{ + std::array retval{}; + int I = 1; + if (retrogade) { + I = -1; + } + double ecc = std::sqrt(eq[1] * eq[1] + eq[2] * eq[2]); + double tmp = std::sqrt(eq[3] * eq[3] + eq[4] * eq[4]); + double zita = std::atan2(eq[2] / ecc, eq[1] / ecc); // [-pi, pi] + if (zita < 0) { + zita += 2 * pi; // [0, 2*pi] + } - retval[1] = ecc; - retval[0] = eq[0] / (1. - ecc * ecc); - retval[2] = half_pi * (1. - I) + 2. * I * std::atan(tmp); - retval[3] = std::atan2(eq[4] / tmp, eq[3] / tmp); // [-pi, pi] - if (retval[3] < 0) { - retval[3] += 2 * pi; // [0, 2*pi] - } - retval[4] = zita - I * retval[3]; // - if (retval[4] < 0) { - retval[4] += 2 * pi; - } else if (retval[4] > 2 * pi) { - retval[4] -= 2 * pi; - } - retval[5] = eq[5] - I * retval[3] - retval[4]; - return retval; + retval[1] = ecc; + retval[0] = eq[0] / (1. - ecc * ecc); + retval[2] = half_pi * (1. - I) + 2. * I * std::atan(tmp); + retval[3] = std::atan2(eq[4] / tmp, eq[3] / tmp); // [-pi, pi] + if (retval[3] < 0) { + retval[3] += 2 * pi; // [0, 2*pi] + } + retval[4] = zita - I * retval[3]; // + if (retval[4] < 0) { + retval[4] += 2 * pi; + } else if (retval[4] > 2 * pi) { + retval[4] -= 2 * pi; + } + retval[5] = eq[5] - I * retval[3] - retval[4]; + return retval; } -std::array par2eq(const std::array &par, bool retrogade) { - std::array eq{}; - int I = 0; - if (retrogade) { - I = -1; - eq[3] = 1. / std::tan(par[2] / 2) * std::cos(par[3]); - eq[4] = 1. / std::tan(par[2] / 2) * std::sin(par[3]); - } else { - I = 1; - eq[3] = std::tan(par[2] / 2) * std::cos(par[3]); - eq[4] = std::tan(par[2] / 2) * std::sin(par[3]); - } - eq[0] = par[0] * (1 - par[1] * par[1]); - eq[1] = par[1] * std::cos(par[4] + I * par[3]); - eq[2] = par[1] * std::sin(par[4] + I * par[3]); - eq[5] = par[5] + par[4] + I * par[3]; - return eq; +std::array par2eq(const std::array &par, bool retrogade) +{ + std::array eq{}; + int I = 0; + if (retrogade) { + I = -1; + eq[3] = 1. / std::tan(par[2] / 2) * std::cos(par[3]); + eq[4] = 1. / std::tan(par[2] / 2) * std::sin(par[3]); + } else { + I = 1; + eq[3] = std::tan(par[2] / 2) * std::cos(par[3]); + eq[4] = std::tan(par[2] / 2) * std::sin(par[3]); + } + eq[0] = par[0] * (1 - par[1] * par[1]); + eq[1] = par[1] * std::cos(par[4] + I * par[3]); + eq[2] = par[1] * std::sin(par[4] + I * par[3]); + eq[5] = par[5] + par[4] + I * par[3]; + return eq; } } // namespace kep3 \ No newline at end of file diff --git a/src/core_astro/ic2eq2ic.cpp b/src/core_astro/ic2eq2ic.cpp index 2674f4b8..333e4d4a 100644 --- a/src/core_astro/ic2eq2ic.cpp +++ b/src/core_astro/ic2eq2ic.cpp @@ -22,134 +22,135 @@ using xt::linalg::cross; using xt::linalg::dot; -namespace kep3 { +namespace kep3 +{ // Implementation following: // Cefola: Equinoctial orbit elements - Application to artificial satellite // orbitsCefola, P., 1972, September. Equinoctial orbit elements-Application to // artificial satellite orbits. In Astrodynamics Conference (p. 937). -std::array ic2eq(const std::array, 2> &pos_vel, - double mu, bool retrogade) { - { - // Switch between the element types. +std::array ic2eq(const std::array, 2> &pos_vel, double mu, bool retrogade) +{ + { + // Switch between the element types. + int I = 0; + if (retrogade) { + I = -1; + } else { + I = 1; + } + // 0 - We prepare a few xtensor constants. + auto r0 = xt::adapt(pos_vel[0]); + auto v0 = xt::adapt(pos_vel[1]); + // The equinoctial reference frame + xt::xtensor_fixed> fv = {0.0, 0.0, 0.0}; + xt::xtensor_fixed> gv = {0.0, 0.0, 0.0}; + + // angular momentum + auto ang = cross(r0, v0); + + // 0 - We compute the semi-major axis + double R0 = xt::linalg::norm(r0); + double V0 = xt::linalg::norm(v0); + double sma = 1. / (2. / R0 - V0 * V0 / mu); + + // 1 - We compute the equinoctial frame + auto w = cross(r0, v0); + w = w / xt::linalg::norm(w); + + double k = w(0) / (1. + I * w(2)); + double h = -w(1) / (1. + I * w(2)); + double den = k * k + h * h + 1; + fv(0) = (1. - k * k + h * h) / den; + fv(1) = (2. * k * h) / den; + fv(2) = (-2. * I * k) / den; + + gv(0) = (2. * I * k * h) / den; + gv(1) = (1. + k * k - h * h) * I / den; + gv(2) = (2. * h) / den; + + // 2 - We compute evett: the eccentricity vector + auto evett = cross(v0, ang) / mu - r0 / R0; // e = (v x h)/mu - r0/R0; + + double g = dot(evett, gv)(0); + double f = dot(evett, fv)(0); + double ecc = xt::linalg::norm(evett); + + // 3 - We compute the true longitude L + // This solution is certainly not the most elegant, but it works and will + // never be singular. + + double det1 = (gv(1) * fv(0) - fv(1) * gv(0)); // xy + double det2 = (gv(2) * fv(0) - fv(2) * gv(0)); // xz + double det3 = (gv(2) * fv(1) - fv(2) * gv(1)); // yz + double max = std::max({std::abs(det1), std::abs(det2), std::abs(det3)}); + + double X = 0., Y = 0.; + if (std::abs(det1) == max) { + X = (gv(1) * r0(0) - gv(0) * r0(1)) / det1; + Y = (-fv(1) * r0(0) + fv(0) * r0(1)) / det1; + } else if (std::abs(det2) == max) { + X = (gv(2) * r0(0) - gv(0) * r0(2)) / det2; + Y = (-fv(2) * r0(0) + fv(0) * r0(2)) / det2; + } else { + X = (gv(2) * r0(1) - gv(1) * r0(2)) / det3; + Y = (-fv(2) * r0(1) + fv(1) * r0(2)) / det3; + } + + double L = std::atan2(Y / R0, X / R0); + + // 5 - We assign the results + return {sma * (1. - ecc * ecc), f, g, h, k, L}; + } +} + +std::array, 2> eq2ic(const std::array &eq, double mu, bool retrogade) +{ + std::array, 2> retval{}; int I = 0; if (retrogade) { - I = -1; + I = -1; } else { - I = 1; + I = 1; } - // 0 - We prepare a few xtensor constants. - auto r0 = xt::adapt(pos_vel[0]); - auto v0 = xt::adapt(pos_vel[1]); - // The equinoctial reference frame - xt::xtensor_fixed> fv = {0.0, 0.0, 0.0}; - xt::xtensor_fixed> gv = {0.0, 0.0, 0.0}; - - // angular momentum - auto ang = cross(r0, v0); - - // 0 - We compute the semi-major axis - double R0 = xt::linalg::norm(r0); - double V0 = xt::linalg::norm(v0); - double sma = 1. / (2. / R0 - V0 * V0 / mu); - - // 1 - We compute the equinoctial frame - auto w = cross(r0, v0); - w = w / xt::linalg::norm(w); - - double k = w(0) / (1. + I * w(2)); - double h = -w(1) / (1. + I * w(2)); - double den = k * k + h * h + 1; - fv(0) = (1. - k * k + h * h) / den; - fv(1) = (2. * k * h) / den; - fv(2) = (-2. * I * k) / den; - - gv(0) = (2. * I * k * h) / den; - gv(1) = (1. + k * k - h * h) * I / den; - gv(2) = (2. * h) / den; - - // 2 - We compute evett: the eccentricity vector - auto evett = cross(v0, ang) / mu - r0 / R0; // e = (v x h)/mu - r0/R0; - - double g = dot(evett, gv)(0); - double f = dot(evett, fv)(0); - double ecc = xt::linalg::norm(evett); - - // 3 - We compute the true longitude L - // This solution is certainly not the most elegant, but it works and will - // never be singular. - - double det1 = (gv(1) * fv(0) - fv(1) * gv(0)); // xy - double det2 = (gv(2) * fv(0) - fv(2) * gv(0)); // xz - double det3 = (gv(2) * fv(1) - fv(2) * gv(1)); // yz - double max = std::max({std::abs(det1), std::abs(det2), std::abs(det3)}); - - double X = 0., Y = 0.; - if (std::abs(det1) == max) { - X = (gv(1) * r0(0) - gv(0) * r0(1)) / det1; - Y = (-fv(1) * r0(0) + fv(0) * r0(1)) / det1; - } else if (std::abs(det2) == max) { - X = (gv(2) * r0(0) - gv(0) * r0(2)) / det2; - Y = (-fv(2) * r0(0) + fv(0) * r0(2)) / det2; - } else { - X = (gv(2) * r0(1) - gv(1) * r0(2)) / det3; - Y = (-fv(2) * r0(1) + fv(1) * r0(2)) / det3; - } - - double L = std::atan2(Y / R0, X / R0); - // 5 - We assign the results - return {sma * (1. - ecc * ecc), f, g, h, k, L}; - } -} + // p = a (1-e^2) will be negative for eccentricities > 1, we here need a + // positive number for the following computations to make sense + double par = std::abs(eq[0]); + double f = eq[1]; + double g = eq[2]; + double h = eq[3]; + double k = eq[4]; + double L = eq[5]; -std::array, 2> eq2ic(const std::array &eq, - double mu, bool retrogade) { - std::array, 2> retval{}; - int I = 0; - if (retrogade) { - I = -1; - } else { - I = 1; - } - - // p = a (1-e^2) will be negative for eccentricities > 1, we here need a - // positive number for the following computations to make sense - double par = std::abs(eq[0]); - double f = eq[1]; - double g = eq[2]; - double h = eq[3]; - double k = eq[4]; - double L = eq[5]; - - // We compute the equinoctial reference frame - double den = k * k + h * h + 1; - double fx = (1 - k * k + h * h) / den; - double fy = (2 * k * h) / den; - double fz = (-2 * I * k) / den; - - double gx = (2 * I * k * h) / den; - double gy = (1 + k * k - h * h) * I / den; - double gz = (2 * h) / den; - - // Auxiliary - double radius = par / (1 + g * std::sin(L) + f * std::cos(L)); - // In the equinoctial reference frame - double X = radius * std::cos(L); - double Y = radius * std::sin(L); - double VX = -std::sqrt(mu / par) * (g + std::sin(L)); - double VY = std::sqrt(mu / par) * (f + std::cos(L)); - - // Results - retval[0][0] = X * fx + Y * gx; - retval[0][1] = X * fy + Y * gy; - retval[0][2] = X * fz + Y * gz; - - retval[1][0] = VX * fx + VY * gx; - retval[1][1] = VX * fy + VY * gy; - retval[1][2] = VX * fz + VY * gz; - - return retval; + // We compute the equinoctial reference frame + double den = k * k + h * h + 1; + double fx = (1 - k * k + h * h) / den; + double fy = (2 * k * h) / den; + double fz = (-2 * I * k) / den; + + double gx = (2 * I * k * h) / den; + double gy = (1 + k * k - h * h) * I / den; + double gz = (2 * h) / den; + + // Auxiliary + double radius = par / (1 + g * std::sin(L) + f * std::cos(L)); + // In the equinoctial reference frame + double X = radius * std::cos(L); + double Y = radius * std::sin(L); + double VX = -std::sqrt(mu / par) * (g + std::sin(L)); + double VY = std::sqrt(mu / par) * (f + std::cos(L)); + + // Results + retval[0][0] = X * fx + Y * gx; + retval[0][1] = X * fy + Y * gy; + retval[0][2] = X * fz + Y * gz; + + retval[1][0] = VX * fx + VY * gx; + retval[1][1] = VX * fy + VY * gy; + retval[1][2] = VX * fz + VY * gz; + + return retval; } } // namespace kep3 \ No newline at end of file diff --git a/src/core_astro/ic2par2ic.cpp b/src/core_astro/ic2par2ic.cpp index 6d19ae0d..ae0b0910 100644 --- a/src/core_astro/ic2par2ic.cpp +++ b/src/core_astro/ic2par2ic.cpp @@ -16,7 +16,8 @@ #include #include -namespace kep3 { +namespace kep3 +{ using xt::linalg::cross; using xt::linalg::dot; @@ -26,66 +27,66 @@ using xt::linalg::dot; // for hyperbolae. The anomalies W, w, f are in [0, 2pi]. Inclination is in [0, // pi]. -std::array -ic2par(const std::array, 2> &pos_vel, double mu) { - // Return value - std::array retval{}; - // 0 - We prepare a few xtensor constants. - xt::xtensor_fixed> k{0.0, 0.0, 1.0}; - auto r0 = xt::adapt(pos_vel[0]); - auto v0 = xt::adapt(pos_vel[1]); - - // 1 - We compute the orbital angular momentum vector - auto h = cross(r0, v0); // h = r0 x v0 - - // 2 - We compute the orbital parameter - auto p = dot(h, h) / mu; // p = h^2 / mu - - // 3 - We compute the vector of the node line - // This operation is singular when inclination is zero, in which case the - // Keplerian orbital parameters are not well defined - auto n = cross(k, h); - n = n / xt::linalg::norm(n); // n = (k x h) / |k x h| - - // 4 - We compute the eccentricity vector - auto R0 = xt::linalg::norm(r0); - auto evett = cross(v0, h) / mu - r0 / R0; // e = (v x h)/mu - r0/R0; - - // The eccentricity is calculated and stored as the second orbital element - retval[1] = xt::linalg::norm(evett); - - // The semi-major axis (positive for ellipses, negative for hyperbolas) is - // calculated and stored as the first orbital element a = p / (1 - e^2) - retval[0] = p(0) / (1 - retval[1] * retval[1]); - - // Inclination is calculated and stored as the third orbital element - // i = acos(hy/h) in [0, pi] - retval[2] = std::acos(h(2) / xt::linalg::norm(h)); - - // Argument of pericentrum is calculated and stored as the fifth orbital - // element. w = acos(n.e)\|n||e| in [0, 2pi] - auto temp = dot(n, evett); - retval[4] = std::acos(temp(0) / retval[1]); - if (evett(2) < 0) { - retval[4] = 2 * pi - retval[4]; - } - // Argument of longitude is calculated and stored as the fourth orbital - // element in [0, 2pi] - retval[3] = std::acos(n(0)); - if (n(1) < 0) { - retval[3] = 2 * pi - retval[3]; - } - - // 4 - We compute ni: the true anomaly in [0, 2pi] - temp = dot(evett, r0); - auto f = std::acos(temp(0) / retval[1] / R0); - - temp = dot(r0, v0); - if (temp(0) < 0.0) { - f = 2 * pi - f; - } - retval[5] = f; - return retval; +std::array ic2par(const std::array, 2> &pos_vel, double mu) +{ + // Return value + std::array retval{}; + // 0 - We prepare a few xtensor constants. + xt::xtensor_fixed> k{0.0, 0.0, 1.0}; + auto r0 = xt::adapt(pos_vel[0]); + auto v0 = xt::adapt(pos_vel[1]); + + // 1 - We compute the orbital angular momentum vector + auto h = cross(r0, v0); // h = r0 x v0 + + // 2 - We compute the orbital parameter + auto p = dot(h, h) / mu; // p = h^2 / mu + + // 3 - We compute the vector of the node line + // This operation is singular when inclination is zero, in which case the + // Keplerian orbital parameters are not well defined + auto n = cross(k, h); + n = n / xt::linalg::norm(n); // n = (k x h) / |k x h| + + // 4 - We compute the eccentricity vector + auto R0 = xt::linalg::norm(r0); + auto evett = cross(v0, h) / mu - r0 / R0; // e = (v x h)/mu - r0/R0; + + // The eccentricity is calculated and stored as the second orbital element + retval[1] = xt::linalg::norm(evett); + + // The semi-major axis (positive for ellipses, negative for hyperbolas) is + // calculated and stored as the first orbital element a = p / (1 - e^2) + retval[0] = p(0) / (1 - retval[1] * retval[1]); + + // Inclination is calculated and stored as the third orbital element + // i = acos(hy/h) in [0, pi] + retval[2] = std::acos(h(2) / xt::linalg::norm(h)); + + // Argument of pericentrum is calculated and stored as the fifth orbital + // element. w = acos(n.e)\|n||e| in [0, 2pi] + auto temp = dot(n, evett); + retval[4] = std::acos(temp(0) / retval[1]); + if (evett(2) < 0) { + retval[4] = 2 * pi - retval[4]; + } + // Argument of longitude is calculated and stored as the fourth orbital + // element in [0, 2pi] + retval[3] = std::acos(n(0)); + if (n(1) < 0) { + retval[3] = 2 * pi - retval[3]; + } + + // 4 - We compute ni: the true anomaly in [0, 2pi] + temp = dot(evett, r0); + auto f = std::acos(temp(0) / retval[1] / R0); + + temp = dot(r0, v0); + if (temp(0) < 0.0) { + f = 2 * pi - f; + } + retval[5] = f; + return retval; } // keplerian osculating elements [a,e,i,W,w,f] -> r,v. @@ -94,69 +95,66 @@ ic2par(const std::array, 2> &pos_vel, double mu) { // for ellipses, negative for hyperbolae. // The anomalies W, w and E must be in [0, 2pi] and inclination in [0, pi]. -std::array, 2> par2ic(const std::array &par, - double mu) { - // Return values - std::array pos{}; - std::array vel{}; - auto pos_xt = xt::adapt(pos, {3u, 1u}); - auto vel_xt = xt::adapt(vel, {3u, 1u}); - - // Rename some variables for readibility - double sma = par[0]; - double ecc = par[1]; - double inc = par[2]; - double omg = par[3]; - double omp = par[4]; - double f = par[5]; - - if (sma * (1 - ecc) < 0) { - throw std::domain_error("par2ic was called with ecc and sma not compatible " - "with the convention a<0 -> e>1 [a>0 -> e<1]."); - } - double cosf = std::cos(f); - if (ecc > 1 && cosf < -1 / ecc) { - throw std::domain_error("par2ic was called for an hyperbola but the true " - "anomaly is beyond asymptotes (cosf<-1/e)"); - } - - // 1 - We start by evaluating position and velocity in the perifocal reference - // system - double p = sma * (1.0 - ecc * ecc); - double r = p / (1.0 + ecc * std::cos(f)); - double h = std::sqrt(p * mu); - double sinf = std::sin(f); - double x_per = r * cosf; - double y_per = r * sinf; - double xdot_per = -mu / h * sinf; - double ydot_per = mu / h * (ecc + cosf); - - // 2 - We then built the rotation matrix from perifocal reference frame to - // inertial - double cosomg = std::cos(omg); - double cosomp = std::cos(omp); - double sinomg = std::sin(omg); - double sinomp = std::sin(omp); - double cosi = std::cos(inc); - double sini = std::sin(inc); - - xt::xtensor_fixed> R = { - {cosomg * cosomp - sinomg * sinomp * cosi, - -cosomg * sinomp - sinomg * cosomp * cosi, sinomg * sini}, - {sinomg * cosomp + cosomg * sinomp * cosi, - -sinomg * sinomp + cosomg * cosomp * cosi, -cosomg * sini}, - {sinomp * sini, cosomp * sini, cosi}}; - - // 3 - We end by transforming according to this rotation matrix - xt::xtensor_fixed> pos_per{{x_per}, {y_per}, {0.0}}; - xt::xtensor_fixed> vel_per{ - {xdot_per}, {ydot_per}, {0.0}}; - - // The following lines, since use xtensors adapted to pos and vel, will change - // pos and vel. - pos_xt = xt::linalg::dot(R, pos_per); - vel_xt = xt::linalg::dot(R, vel_per); - - return {pos, vel}; +std::array, 2> par2ic(const std::array &par, double mu) +{ + // Return values + std::array pos{}; + std::array vel{}; + auto pos_xt = xt::adapt(pos, {3u, 1u}); + auto vel_xt = xt::adapt(vel, {3u, 1u}); + + // Rename some variables for readibility + double sma = par[0]; + double ecc = par[1]; + double inc = par[2]; + double omg = par[3]; + double omp = par[4]; + double f = par[5]; + + if (sma * (1 - ecc) < 0) { + throw std::domain_error("par2ic was called with ecc and sma not compatible " + "with the convention a<0 -> e>1 [a>0 -> e<1]."); + } + double cosf = std::cos(f); + if (ecc > 1 && cosf < -1 / ecc) { + throw std::domain_error("par2ic was called for an hyperbola but the true " + "anomaly is beyond asymptotes (cosf<-1/e)"); + } + + // 1 - We start by evaluating position and velocity in the perifocal reference + // system + double p = sma * (1.0 - ecc * ecc); + double r = p / (1.0 + ecc * std::cos(f)); + double h = std::sqrt(p * mu); + double sinf = std::sin(f); + double x_per = r * cosf; + double y_per = r * sinf; + double xdot_per = -mu / h * sinf; + double ydot_per = mu / h * (ecc + cosf); + + // 2 - We then built the rotation matrix from perifocal reference frame to + // inertial + double cosomg = std::cos(omg); + double cosomp = std::cos(omp); + double sinomg = std::sin(omg); + double sinomp = std::sin(omp); + double cosi = std::cos(inc); + double sini = std::sin(inc); + + xt::xtensor_fixed> R + = {{cosomg * cosomp - sinomg * sinomp * cosi, -cosomg * sinomp - sinomg * cosomp * cosi, sinomg * sini}, + {sinomg * cosomp + cosomg * sinomp * cosi, -sinomg * sinomp + cosomg * cosomp * cosi, -cosomg * sini}, + {sinomp * sini, cosomp * sini, cosi}}; + + // 3 - We end by transforming according to this rotation matrix + xt::xtensor_fixed> pos_per{{x_per}, {y_per}, {0.0}}; + xt::xtensor_fixed> vel_per{{xdot_per}, {ydot_per}, {0.0}}; + + // The following lines, since use xtensors adapted to pos and vel, will change + // pos and vel. + pos_xt = xt::linalg::dot(R, pos_per); + vel_xt = xt::linalg::dot(R, vel_per); + + return {pos, vel}; } } // namespace kep3 \ No newline at end of file diff --git a/src/core_astro/propagate_lagrangian.cpp b/src/core_astro/propagate_lagrangian.cpp index 8359f695..098be2e3 100644 --- a/src/core_astro/propagate_lagrangian.cpp +++ b/src/core_astro/propagate_lagrangian.cpp @@ -21,7 +21,8 @@ #include #include -namespace kep3 { +namespace kep3 +{ /// Lagrangian propagation /** @@ -30,112 +31,103 @@ namespace kep3 { * numerical technique. All units systems can be used, as long * as the input parameters are all expressed in the same system. */ -void propagate_lagrangian(std::array, 2> &pos_vel_0, - const double dt, const double mu) { - auto &[r0, v0] = pos_vel_0; - double R = std::sqrt(r0[0] * r0[0] + r0[1] * r0[1] + r0[2] * r0[2]); - double V = std::sqrt(v0[0] * v0[0] + v0[1] * v0[1] + v0[2] * v0[2]); - double energy = (V * V / 2 - mu / R); - double a = -mu / 2.0 / energy; // will be negative for hyperbolae - double sqrta = 0.; - double F = 0., G = 0., Ft = 0., Gt = 0.; - double sigma0 = - (r0[0] * v0[0] + r0[1] * v0[1] + r0[2] * v0[2]) / std::sqrt(mu); - - if (a > 0) { // Solve Kepler's equation in DE, elliptical case - sqrta = std::sqrt(a); - double DM = std::sqrt(mu / std::pow(a, 3)) * dt; - double sinDM = std::sin(DM), cosDM = std::cos(DM); - // Here we use the atan2 to recover the mean anomaly difference in the - // [0,2pi] range. This makes sure that for high value of M no catastrophic - // cancellation occurs, as would be the case using std::fmod(DM, 2pi) - double DM_cropped = std::atan2(sinDM, cosDM); - if (DM_cropped < 0) { - DM_cropped += 2 * kep3::pi; - } - double s0 = sigma0 / sqrta; - double c0 = (1 - R / a); - // This initial guess was developed applying Lagrange expansion theorem to - // the Kepler's equation in DE. We stopped at 3rd order. - double IG = - DM_cropped + c0 * sinDM - s0 * (1 - cosDM) + - (c0 * cosDM - s0 * sinDM) * (c0 * sinDM + s0 * cosDM - s0) + - 0.5 * (c0 * sinDM + s0 * cosDM - s0) * - (2 * std::pow(c0 * cosDM - s0 * sinDM, 2) - - (c0 * sinDM + s0 * cosDM - s0) * (c0 * sinDM + s0 * cosDM)); - - // Solve Kepler Equation for ellipses in DE (eccentric anomaly difference) - const int digits = std::numeric_limits::digits; - std::uintmax_t max_iter = 100u; - // NOTE: Halley iterates may result into instabilities (specially with a - // poor IG) - - double DE = boost::math::tools::newton_raphson_iterate( - [DM_cropped, sigma0, sqrta, a, R](double DE) { - return std::make_tuple(kepDE(DE, DM_cropped, sigma0, sqrta, a, R), - d_kepDE(DE, sigma0, sqrta, a, R)); - }, - IG, IG - pi, IG + pi, digits, max_iter); - if (max_iter == 100u) { - throw std::domain_error(fmt::format( - "Maximum number of iterations exceeded when solving Kepler's " - "equation for the eccentric anomaly in propagate_lagrangian.\n" - "DM={}\nsigma0={}\nsqrta={}\na={}\nR={}\nDE={}", - DM, sigma0, sqrta, a, R, DE)); +void propagate_lagrangian(std::array, 2> &pos_vel_0, const double dt, const double mu) +{ + auto &[r0, v0] = pos_vel_0; + double R = std::sqrt(r0[0] * r0[0] + r0[1] * r0[1] + r0[2] * r0[2]); + double V = std::sqrt(v0[0] * v0[0] + v0[1] * v0[1] + v0[2] * v0[2]); + double energy = (V * V / 2 - mu / R); + double a = -mu / 2.0 / energy; // will be negative for hyperbolae + double sqrta = 0.; + double F = 0., G = 0., Ft = 0., Gt = 0.; + double sigma0 = (r0[0] * v0[0] + r0[1] * v0[1] + r0[2] * v0[2]) / std::sqrt(mu); + + if (a > 0) { // Solve Kepler's equation in DE, elliptical case + sqrta = std::sqrt(a); + double DM = std::sqrt(mu / std::pow(a, 3)) * dt; + double sinDM = std::sin(DM), cosDM = std::cos(DM); + // Here we use the atan2 to recover the mean anomaly difference in the + // [0,2pi] range. This makes sure that for high value of M no catastrophic + // cancellation occurs, as would be the case using std::fmod(DM, 2pi) + double DM_cropped = std::atan2(sinDM, cosDM); + if (DM_cropped < 0) { + DM_cropped += 2 * kep3::pi; + } + double s0 = sigma0 / sqrta; + double c0 = (1 - R / a); + // This initial guess was developed applying Lagrange expansion theorem to + // the Kepler's equation in DE. We stopped at 3rd order. + double IG = DM_cropped + c0 * sinDM - s0 * (1 - cosDM) + + (c0 * cosDM - s0 * sinDM) * (c0 * sinDM + s0 * cosDM - s0) + + 0.5 * (c0 * sinDM + s0 * cosDM - s0) + * (2 * std::pow(c0 * cosDM - s0 * sinDM, 2) + - (c0 * sinDM + s0 * cosDM - s0) * (c0 * sinDM + s0 * cosDM)); + + // Solve Kepler Equation for ellipses in DE (eccentric anomaly difference) + const int digits = std::numeric_limits::digits; + std::uintmax_t max_iter = 100u; + // NOTE: Halley iterates may result into instabilities (specially with a + // poor IG) + + double DE = boost::math::tools::newton_raphson_iterate( + [DM_cropped, sigma0, sqrta, a, R](double DE) { + return std::make_tuple(kepDE(DE, DM_cropped, sigma0, sqrta, a, R), d_kepDE(DE, sigma0, sqrta, a, R)); + }, + IG, IG - pi, IG + pi, digits, max_iter); + if (max_iter == 100u) { + throw std::domain_error(fmt::format("Maximum number of iterations exceeded when solving Kepler's " + "equation for the eccentric anomaly in propagate_lagrangian.\n" + "DM={}\nsigma0={}\nsqrta={}\na={}\nR={}\nDE={}", + DM, sigma0, sqrta, a, R, DE)); + } + double r = a + (R - a) * std::cos(DE) + sigma0 * sqrta * std::sin(DE); + + // Lagrange coefficients + F = 1 - a / R * (1 - std::cos(DE)); + G = a * sigma0 / std::sqrt(mu) * (1 - std::cos(DE)) + R * std::sqrt(a / mu) * std::sin(DE); + Ft = -std::sqrt(mu * a) / (r * R) * std::sin(DE); + Gt = 1 - a / r * (1 - std::cos(DE)); + } else { // Solve Kepler's equation in DH, hyperbolic case + sqrta = std::sqrt(-a); + double DN = std::sqrt(-mu / a / a / a) * dt; + double IG = 0.; + dt > 0. ? IG = 1. : IG = -1.; // TODO(darioizzo): find a better initial guess. + // I tried with 0 and DN (both have numercial + // problems and result in exceptions) + + // Solve Kepler Equation for ellipses in DH (hyperbolic anomaly difference) + const int digits = std::numeric_limits::digits; + std::uintmax_t max_iter = 100u; + // NOTE: Halley iterates may result into instabilities (specially with a + // poor IG) + double DH = boost::math::tools::newton_raphson_iterate( + [DN, sigma0, sqrta, a, R](double DH) { + return std::make_tuple(kepDH(DH, DN, sigma0, sqrta, a, R), d_kepDH(DH, sigma0, sqrta, a, R)); + }, + IG, IG - 50, IG + 50, digits, + max_iter); // TODO (dario): study this hyperbolic equation in more + // details as to provide decent and well proved bounds + if (max_iter == 100u) { + throw std::domain_error(fmt::format("Maximum number of iterations exceeded when solving Kepler's " + "equation for the hyperbolic anomaly in propagate_lagrangian.\n" + "DN={}\nsigma0={}\nsqrta={}\na={}\nR={}\nDH={}", + DN, sigma0, sqrta, a, R, DH)); + } + + double r = a + (R - a) * std::cosh(DH) + sigma0 * sqrta * std::sinh(DH); + + // Lagrange coefficients + F = 1. - a / R * (1. - std::cosh(DH)); + G = a * sigma0 / std::sqrt(mu) * (1. - std::cosh(DH)) + R * std::sqrt(-a / mu) * std::sinh(DH); + Ft = -std::sqrt(-mu * a) / (r * R) * std::sinh(DH); + Gt = 1. - a / r * (1. - std::cosh(DH)); } - double r = a + (R - a) * std::cos(DE) + sigma0 * sqrta * std::sin(DE); - // Lagrange coefficients - F = 1 - a / R * (1 - std::cos(DE)); - G = a * sigma0 / std::sqrt(mu) * (1 - std::cos(DE)) + - R * std::sqrt(a / mu) * std::sin(DE); - Ft = -std::sqrt(mu * a) / (r * R) * std::sin(DE); - Gt = 1 - a / r * (1 - std::cos(DE)); - } else { // Solve Kepler's equation in DH, hyperbolic case - sqrta = std::sqrt(-a); - double DN = std::sqrt(-mu / a / a / a) * dt; - double IG = 0.; - dt > 0. ? IG = 1. - : IG = -1.; // TODO(darioizzo): find a better initial guess. - // I tried with 0 and DN (both have numercial - // problems and result in exceptions) - - // Solve Kepler Equation for ellipses in DH (hyperbolic anomaly difference) - const int digits = std::numeric_limits::digits; - std::uintmax_t max_iter = 100u; - // NOTE: Halley iterates may result into instabilities (specially with a - // poor IG) - double DH = boost::math::tools::newton_raphson_iterate( - [DN, sigma0, sqrta, a, R](double DH) { - return std::make_tuple(kepDH(DH, DN, sigma0, sqrta, a, R), - d_kepDH(DH, sigma0, sqrta, a, R)); - }, - IG, IG - 50, IG + 50, digits, - max_iter); // TODO (dario): study this hyperbolic equation in more - // details as to provide decent and well proved bounds - if (max_iter == 100u) { - throw std::domain_error(fmt::format( - "Maximum number of iterations exceeded when solving Kepler's " - "equation for the hyperbolic anomaly in propagate_lagrangian.\n" - "DN={}\nsigma0={}\nsqrta={}\na={}\nR={}\nDH={}", - DN, sigma0, sqrta, a, R, DH)); + double temp[3] = {r0[0], r0[1], r0[2]}; + for (auto i = 0u; i < 3; i++) { + r0[i] = F * r0[i] + G * v0[i]; + v0[i] = Ft * temp[i] + Gt * v0[i]; } - - double r = a + (R - a) * std::cosh(DH) + sigma0 * sqrta * std::sinh(DH); - - // Lagrange coefficients - F = 1. - a / R * (1. - std::cosh(DH)); - G = a * sigma0 / std::sqrt(mu) * (1. - std::cosh(DH)) + - R * std::sqrt(-a / mu) * std::sinh(DH); - Ft = -std::sqrt(-mu * a) / (r * R) * std::sinh(DH); - Gt = 1. - a / r * (1. - std::cosh(DH)); - } - - double temp[3] = {r0[0], r0[1], r0[2]}; - for (auto i = 0u; i < 3; i++) { - r0[i] = F * r0[i] + G * v0[i]; - v0[i] = Ft * temp[i] + Gt * v0[i]; - } } /// Universial Variables version @@ -144,82 +136,80 @@ void propagate_lagrangian(std::array, 2> &pos_vel_0, * internally makes use of universal variables formulation for the Lagrange * Coefficients. */ -void propagate_lagrangian_u(std::array, 2> &pos_vel_0, - const double dt, const double mu) { // NOLINT - // If time is negative we need to invert time and velocities. Unlike the other - // formulation of the propagate lagrangian we cannot rely on negative times to - // automatically mean back-propagation - double dt_copy = dt; - auto &[r0, v0] = pos_vel_0; - - if (dt < 0) { - dt_copy = -dt; - v0[0] = -v0[0]; - v0[1] = -v0[1]; - v0[2] = -v0[2]; - } - - double F = 0., G = 0., Ft = 0., Gt = 0.; - double R0 = std::sqrt(r0[0] * r0[0] + r0[1] * r0[1] + r0[2] * r0[2]); - double V0 = std::sqrt(v0[0] * v0[0] + v0[1] * v0[1] + v0[2] * v0[2]); - // the reciprocal of the semi-major axis - double alpha = 2 / R0 - V0 * V0 / mu; - // initial radial velocity - double VR0 = (r0[0] * v0[0] + r0[1] * v0[1] + r0[2] * v0[2]) / R0; - - // solve kepler's equation in the universal anomaly DS - double IG = 0; - alpha > 0. ? IG = std::sqrt(mu) * dt_copy * std::abs(alpha) - : IG = 3.; // TODO(darioizzo): initial guess for the universal - // anomaly. For hyperbolas is 3 .... can be better? - - // Solve Kepler Equation in DS (univrsal anomaly difference) - const int digits = std::numeric_limits::digits; - std::uintmax_t max_iter = 100u; - // NOTE: Halley iterates may result into instabilities (specially with a poor - // IG) - double DS = boost::math::tools::newton_raphson_iterate( - [dt_copy, R0, VR0, alpha, mu](double DS) { - return std::make_tuple(kepDS(DS, dt_copy, R0, VR0, alpha, mu), - d_kepDS(DS, R0, VR0, alpha, mu)); - }, - IG, IG - 2 * pi, IG + 2 * pi, digits, - max_iter); // limiting the IG error within - // only pi will not work. - if (max_iter == 100u) { - throw std::domain_error( - "Maximum number of iterations exceeded when solving Kepler's " - "equation for the universal anomaly in propagate_lagrangian_u."); - } - // evaluate the lagrangian coefficients F and G - double S = stumpff_s(alpha * DS * DS); - double C = stumpff_c(alpha * DS * DS); - // - double z = alpha * DS * DS; - F = 1 - DS * DS / R0 * C; - G = dt_copy - 1 / std::sqrt(mu) * DS * DS * DS * S; - - double r0_copy[3] = {r0[0], r0[1], r0[2]}; - // compute the final position - r0[0] = F * r0[0] + G * v0[0]; - r0[1] = F * r0[1] + G * v0[1]; - r0[2] = F * r0[2] + G * v0[2]; - double RF = std::sqrt(r0[0] * r0[0] + r0[1] * r0[1] + r0[2] * r0[2]); - - // compute the lagrangian coefficients Ft, Gt - Ft = std::sqrt(mu) / RF / R0 * (z * S - 1) * DS; - Gt = 1 - DS * DS / RF * C; - - // compute the final velocity - v0[0] = Ft * r0_copy[0] + Gt * v0[0]; - v0[1] = Ft * r0_copy[1] + Gt * v0[1]; - v0[2] = Ft * r0_copy[2] + Gt * v0[2]; +void propagate_lagrangian_u(std::array, 2> &pos_vel_0, const double dt, const double mu) +{ // NOLINT + // If time is negative we need to invert time and velocities. Unlike the other + // formulation of the propagate lagrangian we cannot rely on negative times to + // automatically mean back-propagation + double dt_copy = dt; + auto &[r0, v0] = pos_vel_0; + + if (dt < 0) { + dt_copy = -dt; + v0[0] = -v0[0]; + v0[1] = -v0[1]; + v0[2] = -v0[2]; + } - if (dt < 0) { - v0[0] = -v0[0]; - v0[1] = -v0[1]; - v0[2] = -v0[2]; - } + double F = 0., G = 0., Ft = 0., Gt = 0.; + double R0 = std::sqrt(r0[0] * r0[0] + r0[1] * r0[1] + r0[2] * r0[2]); + double V0 = std::sqrt(v0[0] * v0[0] + v0[1] * v0[1] + v0[2] * v0[2]); + // the reciprocal of the semi-major axis + double alpha = 2 / R0 - V0 * V0 / mu; + // initial radial velocity + double VR0 = (r0[0] * v0[0] + r0[1] * v0[1] + r0[2] * v0[2]) / R0; + + // solve kepler's equation in the universal anomaly DS + double IG = 0; + alpha > 0. ? IG = std::sqrt(mu) * dt_copy * std::abs(alpha) + : IG = 3.; // TODO(darioizzo): initial guess for the universal + // anomaly. For hyperbolas is 3 .... can be better? + + // Solve Kepler Equation in DS (univrsal anomaly difference) + const int digits = std::numeric_limits::digits; + std::uintmax_t max_iter = 100u; + // NOTE: Halley iterates may result into instabilities (specially with a poor + // IG) + double DS = boost::math::tools::newton_raphson_iterate( + [dt_copy, R0, VR0, alpha, mu](double DS) { + return std::make_tuple(kepDS(DS, dt_copy, R0, VR0, alpha, mu), d_kepDS(DS, R0, VR0, alpha, mu)); + }, + IG, IG - 2 * pi, IG + 2 * pi, digits, + max_iter); // limiting the IG error within + // only pi will not work. + if (max_iter == 100u) { + throw std::domain_error("Maximum number of iterations exceeded when solving Kepler's " + "equation for the universal anomaly in propagate_lagrangian_u."); + } + // evaluate the lagrangian coefficients F and G + double S = stumpff_s(alpha * DS * DS); + double C = stumpff_c(alpha * DS * DS); + // + double z = alpha * DS * DS; + F = 1 - DS * DS / R0 * C; + G = dt_copy - 1 / std::sqrt(mu) * DS * DS * DS * S; + + double r0_copy[3] = {r0[0], r0[1], r0[2]}; + // compute the final position + r0[0] = F * r0[0] + G * v0[0]; + r0[1] = F * r0[1] + G * v0[1]; + r0[2] = F * r0[2] + G * v0[2]; + double RF = std::sqrt(r0[0] * r0[0] + r0[1] * r0[1] + r0[2] * r0[2]); + + // compute the lagrangian coefficients Ft, Gt + Ft = std::sqrt(mu) / RF / R0 * (z * S - 1) * DS; + Gt = 1 - DS * DS / RF * C; + + // compute the final velocity + v0[0] = Ft * r0_copy[0] + Gt * v0[0]; + v0[1] = Ft * r0_copy[1] + Gt * v0[1]; + v0[2] = Ft * r0_copy[2] + Gt * v0[2]; + + if (dt < 0) { + v0[0] = -v0[0]; + v0[1] = -v0[1]; + v0[2] = -v0[2]; + } } /// Keplerian (not using the lagrangian coefficients) propagation @@ -229,29 +219,29 @@ void propagate_lagrangian_u(std::array, 2> &pos_vel_0, * M0 then Mt, etc.. It only here for study purposes as its x10 slower (strange * such a high factor ..investigate?) */ -void propagate_keplerian(std::array, 2> &pos_vel_0, - const double dt, const double mu) { // NOLINT - - // 1 - Compute the orbital parameters at t0 - auto par = kep3::ic2par(pos_vel_0, mu); - if (par[0] > 0) { - // 2e - Compute the mean anomalies - double n = std::sqrt(mu / par[0] / par[0] / par[0]); - double M0 = kep3::f2m(par[5], par[1]); - double Mf = M0 + n * dt; - // 3e - Update elements (here Kepler's equation gets solved) - par[5] = kep3::m2f(Mf, par[1]); - } else { - // 2h - Compute the mean hyperbolic anomalies - double n = std::sqrt(-mu / par[0] / par[0] / par[0]); - double N0 = kep3::f2n(par[5], par[1]); - double Nf = N0 + n * dt; - // 3h - Update elements (here Kepler's equation gets solved in its - // hyperbolic version) - par[5] = kep3::n2f(Nf, par[1]); - } - // Update posvel - pos_vel_0 = kep3::par2ic(par, mu); +void propagate_keplerian(std::array, 2> &pos_vel_0, const double dt, const double mu) +{ // NOLINT + + // 1 - Compute the orbital parameters at t0 + auto par = kep3::ic2par(pos_vel_0, mu); + if (par[0] > 0) { + // 2e - Compute the mean anomalies + double n = std::sqrt(mu / par[0] / par[0] / par[0]); + double M0 = kep3::f2m(par[5], par[1]); + double Mf = M0 + n * dt; + // 3e - Update elements (here Kepler's equation gets solved) + par[5] = kep3::m2f(Mf, par[1]); + } else { + // 2h - Compute the mean hyperbolic anomalies + double n = std::sqrt(-mu / par[0] / par[0] / par[0]); + double N0 = kep3::f2n(par[5], par[1]); + double Nf = N0 + n * dt; + // 3h - Update elements (here Kepler's equation gets solved in its + // hyperbolic version) + par[5] = kep3::n2f(Nf, par[1]); + } + // Update posvel + pos_vel_0 = kep3::par2ic(par, mu); } } // namespace kep3 \ No newline at end of file diff --git a/src/detail/type_name.cpp b/src/detail/type_name.cpp index 2c7bd176..27d17da8 100644 --- a/src/detail/type_name.cpp +++ b/src/detail/type_name.cpp @@ -21,28 +21,29 @@ #include -namespace kep3::detail { +namespace kep3::detail +{ -std::string demangle_from_typeid(const char *s) { +std::string demangle_from_typeid(const char *s) +{ #if defined(__GNUC__) || (defined(__clang__) && !defined(_MSC_VER)) - // NOTE: wrap std::free() in a local lambda, so we avoid - // potential ambiguities when taking the address of std::free(). - // See: - // https://stackoverflow.com/questions/27440953/stdunique-ptr-for-c-functions-that-need-free - // NOLINTNEXTLINE(cppcoreguidelines-owning-memory, hicpp-no-malloc, cppcoreguidelines-no-malloc) - auto deleter = [](void *ptr) { std::free(ptr); }; - - // NOTE: abi::__cxa_demangle will return a pointer allocated by std::malloc, - // which we will delete via std::free(). - std::unique_ptr res{ - ::abi::__cxa_demangle(s, nullptr, nullptr, nullptr), deleter}; - - // NOTE: return the original string if demangling fails. - return res ? std::string(res.get()) : std::string(s); + // NOTE: wrap std::free() in a local lambda, so we avoid + // potential ambiguities when taking the address of std::free(). + // See: + // https://stackoverflow.com/questions/27440953/stdunique-ptr-for-c-functions-that-need-free + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory, hicpp-no-malloc, cppcoreguidelines-no-malloc) + auto deleter = [](void *ptr) { std::free(ptr); }; + + // NOTE: abi::__cxa_demangle will return a pointer allocated by std::malloc, + // which we will delete via std::free(). + std::unique_ptr res{::abi::__cxa_demangle(s, nullptr, nullptr, nullptr), deleter}; + + // NOTE: return the original string if demangling fails. + return res ? std::string(res.get()) : std::string(s); #else - // If no demangling is available, just return the mangled name. - // NOTE: MSVC already returns the demangled name from typeid. - return std::string(s); + // If no demangling is available, just return the mangled name. + // NOTE: MSVC already returns the demangled name from typeid. + return std::string(s); #endif } diff --git a/src/epoch.cpp b/src/epoch.cpp index ab6c7592..faeee65f 100644 --- a/src/epoch.cpp +++ b/src/epoch.cpp @@ -10,8 +10,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -20,7 +20,6 @@ #include #include - #include "kep3/core_astro/convert_julian_dates.hpp" #include "kep3/epoch.hpp" @@ -30,8 +29,7 @@ namespace kep3 /** * @brief Constructs a default epoch . */ -epoch::epoch() - : tp{kep_clock::ref_epoch} {} +epoch::epoch() : tp{kep_clock::ref_epoch} {} /** * @brief Constructs an epoch from a non-Gregorian date. @@ -40,8 +38,7 @@ epoch::epoch() since day 0 in the specified calendar. * @param[in] epoch_type epoch::julian_type */ -epoch::epoch(const double epoch_in, const julian_type epoch_type) - : tp{make_tp(epoch_in, epoch_type)} {} +epoch::epoch(const double epoch_in, const julian_type epoch_type) : tp{make_tp(epoch_in, epoch_type)} {} /** * @brief Constructs an epoch from offsets relative to 0 MJD2000. @@ -54,9 +51,11 @@ epoch::epoch(const double epoch_in, const julian_type epoch_type) * @param[in] ms The number of milliseconds. * @param[in] us The number of microseconds. */ -epoch::epoch(const int y, const uint mon, const uint d, const int h, - const int min, const int s, const int ms, const int us) - : tp{make_tp(y, mon, d, h, min, s, ms, us)} {} +epoch::epoch(const int y, const uint mon, const uint d, const int h, const int min, const int s, const int ms, + const int us) + : tp{make_tp(y, mon, d, h, min, s, ms, us)} +{ +} /** * @brief Constructs an epoch from a const reference to a time point. @@ -72,27 +71,28 @@ epoch::epoch(const kep_clock::time_point &time_point) : tp{time_point} {} */ epoch::epoch(kep_clock::time_point &&time_point) : tp{std::move(time_point)} {} -kep_clock::time_point epoch::make_tp(const int y, const uint mon, const uint d, const int h, - const int min, const int s, const int ms, const int us) +kep_clock::time_point epoch::make_tp(const int y, const uint mon, const uint d, const int h, const int min, const int s, + const int ms, const int us) { - return kep_clock::time_point{} + chr::sys_days(chr::year_month_day{chr::year(y) / chr::month(mon) / chr::day(d)} + chr::months{0}).time_since_epoch() + - chr::hours(h) + chr::minutes(min) + chr::seconds(s) + - chr::milliseconds(ms) + chr::microseconds(us); + return kep_clock::time_point{} + + chr::sys_days(chr::year_month_day{chr::year(y) / chr::month(mon) / chr::day(d)} + chr::months{0}) + .time_since_epoch() + + chr::hours(h) + chr::minutes(min) + chr::seconds(s) + chr::milliseconds(ms) + chr::microseconds(us); } -kep_clock::time_point epoch::make_tp(const double epoch_in, - const julian_type epoch_type) { - switch (epoch_type) { - case julian_type::MJD2000: - return epoch::tp_from_days(epoch_in); - case julian_type::MJD: - return epoch::tp_from_days(epoch_in) - 4453401600s; - case julian_type::JD: - return epoch::tp_from_days(epoch_in) - 211813444800s; - default: - throw; - } +kep_clock::time_point epoch::make_tp(const double epoch_in, const julian_type epoch_type) +{ + switch (epoch_type) { + case julian_type::MJD2000: + return epoch::tp_from_days(epoch_in); + case julian_type::MJD: + return epoch::tp_from_days(epoch_in) - 4453401600s; + case julian_type::JD: + return epoch::tp_from_days(epoch_in) - 211813444800s; + default: + throw; + } } /** @@ -100,10 +100,10 @@ kep_clock::time_point epoch::make_tp(const double epoch_in, * * @return A time point */ -constexpr kep_clock::time_point epoch::tp_from_days(const double days) { - return kep_clock::ref_epoch + - chr::duration_cast( - chr::duration>(days)); +constexpr kep_clock::time_point epoch::tp_from_days(const double days) +{ + return kep_clock::ref_epoch + + chr::duration_cast(chr::duration>(days)); } /** @@ -113,17 +113,20 @@ constexpr kep_clock::time_point epoch::tp_from_days(const double days) { * @param tp The time point. * @return A formatted date/time string. */ -std::string epoch::as_utc_string(const kep_clock::time_point& tp) { +std::string epoch::as_utc_string(const kep_clock::time_point &tp) +{ std::stringstream iss; const auto tse{tp.time_since_epoch()}; const auto dp{std::chrono::duration_cast(tse)}; const auto hms{std::chrono::duration_cast(tse - dp)}; const auto us{std::chrono::duration_cast(tse - dp - hms)}; - iss << fmt::format("{:%F}", chr::sys_days(dp)) << "T" << fmt::format("{:%T}", hms) << "." << fmt::format("{:06}", us.count()); + iss << fmt::format("{:%F}", chr::sys_days(dp)) << "T" << fmt::format("{:%T}", hms) << "." + << fmt::format("{:06}", us.count()); return iss.str(); } -std::string epoch::as_utc_string() const { +std::string epoch::as_utc_string() const +{ return epoch::as_utc_string(tp); } @@ -135,20 +138,40 @@ std::string epoch::as_utc_string() const { * * @return Reference to s. */ -std::ostream &operator<<(std::ostream &s, const epoch &ep) { - s << ep.as_utc_string(); - return s; +std::ostream &operator<<(std::ostream &s, const epoch &ep) +{ + s << ep.as_utc_string(); + return s; } -bool operator>(const epoch &c1, const epoch &c2) { return c1.tp > c2.tp; } -bool operator<(const epoch &c1, const epoch &c2) { return c1.tp < c2.tp; } -bool operator>=(const epoch &c1, const epoch &c2) { return c1.tp >= c2.tp; } -bool operator<=(const epoch &c1, const epoch &c2) { return c1.tp <= c2.tp; } -bool operator==(const epoch &c1, const epoch &c2) { return c1.tp == c2.tp; } -bool operator!=(const epoch &c1, const epoch &c2) { return c1.tp != c2.tp; } +bool operator>(const epoch &c1, const epoch &c2) +{ + return c1.tp > c2.tp; +} +bool operator<(const epoch &c1, const epoch &c2) +{ + return c1.tp < c2.tp; +} +bool operator>=(const epoch &c1, const epoch &c2) +{ + return c1.tp >= c2.tp; +} +bool operator<=(const epoch &c1, const epoch &c2) +{ + return c1.tp <= c2.tp; +} +bool operator==(const epoch &c1, const epoch &c2) +{ + return c1.tp == c2.tp; +} +bool operator!=(const epoch &c1, const epoch &c2) +{ + return c1.tp != c2.tp; +} -kep_clock::duration operator-(const epoch &lhs, const epoch &rhs) { - return lhs.tp - rhs.tp; +kep_clock::duration operator-(const epoch &lhs, const epoch &rhs) +{ + return lhs.tp - rhs.tp; } } // namespace kep3 diff --git a/src/lambert_problem.cpp b/src/lambert_problem.cpp index 93933539..6e078d10 100644 --- a/src/lambert_problem.cpp +++ b/src/lambert_problem.cpp @@ -18,7 +18,8 @@ #include #include -namespace kep3 { +namespace kep3 +{ using xt::linalg::cross; @@ -35,272 +36,260 @@ const std::array lambert_problem::default_r2 = {{0.0, 1.0, 0.0}}; * \param[in] cw when true a retrograde orbit is assumed * \param[in] multi_revs maximum number of multirevolutions to compute */ -lambert_problem::lambert_problem(const std::array &r1_a, - const std::array &r2_a, +lambert_problem::lambert_problem(const std::array &r1_a, const std::array &r2_a, double tof, // NOLINT double mu, bool cw, unsigned multi_revs) - : m_r1(r1_a), m_r2(r2_a), m_tof(tof), m_mu(mu), m_has_converged(true), - m_multi_revs(multi_revs) { - // 0 - Sanity checks - if (tof <= 0) { - throw std::domain_error("lambert_problem: Time of flight is negative!"); - } - if (mu <= 0) { - throw std::domain_error( - "lambert_problem: Gravity parameter is zero or negative!"); - } - - // Creating xtensor objects binded to the kep3 arrays - const auto r1 = xt::adapt(r1_a); - const auto r2 = xt::adapt(r2_a); - - // 1 - Getting lambda and T - m_c = xt::linalg::norm(r2 - r1); - - double R1 = xt::linalg::norm(r1); - double R2 = xt::linalg::norm(r2); - m_s = (m_c + R1 + R2) / 2.0; - - auto ir1 = r1 / R1; - auto ir2 = r2 / R2; - auto ih = cross(ir1, ir2); - ih = ih / xt::linalg::norm(ih); - - if (ih(2) == 0) { - throw std::domain_error( - "lambert_problem: The angular momentum vector has no z component, " - "impossible to define automatically clock or " - "counterclockwise"); - } - double lambda2 = 1.0 - m_c / m_s; - m_lambda = std::sqrt(lambda2); - - auto it1 = cross(ih, ir1); - auto it2 = cross(ih, ir2); - it1 = it1 / xt::linalg::norm(it1); - it2 = it2 / xt::linalg::norm(it2); - - if (ih(2) < 0.0) // Transfer angle is larger than 180 degrees as seen from - // above the z axis - { - m_lambda = -m_lambda; - it1 = -it1; - it2 = -it2; - } - if (cw) { // Retrograde motion - m_lambda = -m_lambda; - it1 = -it1; - it2 = -it2; - } - double lambda3 = m_lambda * lambda2; - double T = std::sqrt(2.0 * m_mu / m_s / m_s / m_s) * m_tof; - - // 2 - We now have lambda, T and we will find all x - // 2.1 - Let us first detect the maximum number of revolutions for which there - // exists a solution - m_Nmax = static_cast(T / kep3::pi); - m_Nmax = std::min(m_multi_revs, m_Nmax); - double T00 = std::acos(m_lambda) + m_lambda * std::sqrt(1.0 - lambda2); - double T0 = (T00 + m_Nmax * kep3::pi); - double T1 = 2.0 / 3.0 * (1.0 - lambda3), DT = 0.0, DDT = 0.0, DDDT = 0.0; - if (m_Nmax > 0) { - if (T < T0) { // We use Halley iterations to find xM and TM - int it = 0; - double err = 1.0; - double T_min = T0; - double x_old = 0.0, x_new = 0.0; - while (true) { - dTdx(DT, DDT, DDDT, x_old, T_min); - if (DT != 0.0) { - x_new = x_old - DT * DDT / (DDT * DDT - DT * DDDT / 2.0); - } - err = std::abs(x_old - x_new); - if ((err < 1e-13) || (it > 12)) { - break; - } - x2tof(T_min, x_new, m_Nmax); - x_old = x_new; - it++; - } - if (T_min > T) { - m_Nmax -= 1; - } + : m_r1(r1_a), m_r2(r2_a), m_tof(tof), m_mu(mu), m_has_converged(true), m_multi_revs(multi_revs) +{ + // 0 - Sanity checks + if (tof <= 0) { + throw std::domain_error("lambert_problem: Time of flight is negative!"); + } + if (mu <= 0) { + throw std::domain_error("lambert_problem: Gravity parameter is zero or negative!"); + } + + // Creating xtensor objects binded to the kep3 arrays + const auto r1 = xt::adapt(r1_a); + const auto r2 = xt::adapt(r2_a); + + // 1 - Getting lambda and T + m_c = xt::linalg::norm(r2 - r1); + + double R1 = xt::linalg::norm(r1); + double R2 = xt::linalg::norm(r2); + m_s = (m_c + R1 + R2) / 2.0; + + auto ir1 = r1 / R1; + auto ir2 = r2 / R2; + auto ih = cross(ir1, ir2); + ih = ih / xt::linalg::norm(ih); + + if (ih(2) == 0) { + throw std::domain_error("lambert_problem: The angular momentum vector has no z component, " + "impossible to define automatically clock or " + "counterclockwise"); + } + double lambda2 = 1.0 - m_c / m_s; + m_lambda = std::sqrt(lambda2); + + auto it1 = cross(ih, ir1); + auto it2 = cross(ih, ir2); + it1 = it1 / xt::linalg::norm(it1); + it2 = it2 / xt::linalg::norm(it2); + + if (ih(2) < 0.0) // Transfer angle is larger than 180 degrees as seen from + // above the z axis + { + m_lambda = -m_lambda; + it1 = -it1; + it2 = -it2; } - // We exit this if clause with Nmax being the maximum number of revolutions - // for which there exists a solution. We crop it to m_multi_revs + if (cw) { // Retrograde motion + m_lambda = -m_lambda; + it1 = -it1; + it2 = -it2; + } + double lambda3 = m_lambda * lambda2; + double T = std::sqrt(2.0 * m_mu / m_s / m_s / m_s) * m_tof; + + // 2 - We now have lambda, T and we will find all x + // 2.1 - Let us first detect the maximum number of revolutions for which there + // exists a solution + m_Nmax = static_cast(T / kep3::pi); m_Nmax = std::min(m_multi_revs, m_Nmax); - } - - // 2.2 We now allocate the memory for the output variables - m_v1.resize(static_cast(m_Nmax) * 2 + 1); - m_v2.resize(static_cast(m_Nmax) * 2 + 1); - m_iters.resize(static_cast(m_Nmax) * 2 + 1); - m_x.resize(static_cast(m_Nmax) * 2 + 1); - - // 3 - We may now find all solutions in x,y - // 3.1 0 rev solution - // 3.1.1 initial guess - if (T >= T00) { - m_x[0] = -(T - T00) / (T - T00 + 4); - } else if (T <= T1) { - m_x[0] = T1 * (T1 - T) / (2.0 / 5.0 * (1 - lambda2 * lambda3) * T) + 1; - } else { - m_x[0] = - std::pow((T / T00), 0.69314718055994529 / std::log(T1 / T00)) - 1.0; - } - // 3.1.2 Householder iterations - m_iters[0] = householder(T, m_x[0], 0, 1e-5, 15); - // 3.2 multi rev solutions - double tmp = 0.; - for (std::vector::size_type i = 1u; i < m_Nmax + 1; ++i) { - // 3.2.1 left Householder iterations - tmp = std::pow((static_cast(i) * kep3::pi + kep3::pi) / (8.0 * T), - 2.0 / 3.0); - m_x[2 * i - 1] = (tmp - 1) / (tmp + 1); - m_iters[2 * i - 1] = - householder(T, m_x[2 * i - 1], static_cast(i), 1e-8, 15); - // 3.2.1 right Householder iterations - tmp = std::pow((8.0 * T) / (static_cast(i) * kep3::pi), 2.0 / 3.0); - m_x[2 * i] = (tmp - 1) / (tmp + 1); - m_iters[2ul * i] = - householder(T, m_x[2 * i], static_cast(i), 1e-8, 15); - } - - // 4 - For each found x value we reconstruct the terminal velocities - double gamma = std::sqrt(m_mu * m_s / 2.0); - double rho = (R1 - R2) / m_c; - double sigma = std::sqrt(1 - rho * rho); - double vr1 = 0., vt1 = 0., vr2 = 0., vt2 = 0., y = 0.; - for (size_t i = 0; i < m_x.size(); ++i) { - y = std::sqrt(1.0 - lambda2 + lambda2 * m_x[i] * m_x[i]); - vr1 = - gamma * ((m_lambda * y - m_x[i]) - rho * (m_lambda * y + m_x[i])) / R1; - vr2 = - -gamma * ((m_lambda * y - m_x[i]) + rho * (m_lambda * y + m_x[i])) / R2; - double vt = gamma * sigma * (y + m_lambda * m_x[i]); - vt1 = vt / R1; - vt2 = vt / R2; - for (auto j = 0lu; j < 3lu; ++j) { - m_v1[i][j] = vr1 * ir1[j] + vt1 * it1[j]; + double T00 = std::acos(m_lambda) + m_lambda * std::sqrt(1.0 - lambda2); + double T0 = (T00 + m_Nmax * kep3::pi); + double T1 = 2.0 / 3.0 * (1.0 - lambda3), DT = 0.0, DDT = 0.0, DDDT = 0.0; + if (m_Nmax > 0) { + if (T < T0) { // We use Halley iterations to find xM and TM + int it = 0; + double err = 1.0; + double T_min = T0; + double x_old = 0.0, x_new = 0.0; + while (true) { + dTdx(DT, DDT, DDDT, x_old, T_min); + if (DT != 0.0) { + x_new = x_old - DT * DDT / (DDT * DDT - DT * DDDT / 2.0); + } + err = std::abs(x_old - x_new); + if ((err < 1e-13) || (it > 12)) { + break; + } + x2tof(T_min, x_new, m_Nmax); + x_old = x_new; + it++; + } + if (T_min > T) { + m_Nmax -= 1; + } + } + // We exit this if clause with Nmax being the maximum number of revolutions + // for which there exists a solution. We crop it to m_multi_revs + m_Nmax = std::min(m_multi_revs, m_Nmax); + } + + // 2.2 We now allocate the memory for the output variables + m_v1.resize(static_cast(m_Nmax) * 2 + 1); + m_v2.resize(static_cast(m_Nmax) * 2 + 1); + m_iters.resize(static_cast(m_Nmax) * 2 + 1); + m_x.resize(static_cast(m_Nmax) * 2 + 1); + + // 3 - We may now find all solutions in x,y + // 3.1 0 rev solution + // 3.1.1 initial guess + if (T >= T00) { + m_x[0] = -(T - T00) / (T - T00 + 4); + } else if (T <= T1) { + m_x[0] = T1 * (T1 - T) / (2.0 / 5.0 * (1 - lambda2 * lambda3) * T) + 1; + } else { + m_x[0] = std::pow((T / T00), 0.69314718055994529 / std::log(T1 / T00)) - 1.0; + } + // 3.1.2 Householder iterations + m_iters[0] = householder(T, m_x[0], 0, 1e-5, 15); + // 3.2 multi rev solutions + double tmp = 0.; + for (std::vector::size_type i = 1u; i < m_Nmax + 1; ++i) { + // 3.2.1 left Householder iterations + tmp = std::pow((static_cast(i) * kep3::pi + kep3::pi) / (8.0 * T), 2.0 / 3.0); + m_x[2 * i - 1] = (tmp - 1) / (tmp + 1); + m_iters[2 * i - 1] = householder(T, m_x[2 * i - 1], static_cast(i), 1e-8, 15); + // 3.2.1 right Householder iterations + tmp = std::pow((8.0 * T) / (static_cast(i) * kep3::pi), 2.0 / 3.0); + m_x[2 * i] = (tmp - 1) / (tmp + 1); + m_iters[2ul * i] = householder(T, m_x[2 * i], static_cast(i), 1e-8, 15); } - for (auto j = 0lu; j < 3lu; ++j) { - m_v2[i][j] = vr2 * ir2[j] + vt2 * it2[j]; + + // 4 - For each found x value we reconstruct the terminal velocities + double gamma = std::sqrt(m_mu * m_s / 2.0); + double rho = (R1 - R2) / m_c; + double sigma = std::sqrt(1 - rho * rho); + double vr1 = 0., vt1 = 0., vr2 = 0., vt2 = 0., y = 0.; + for (size_t i = 0; i < m_x.size(); ++i) { + y = std::sqrt(1.0 - lambda2 + lambda2 * m_x[i] * m_x[i]); + vr1 = gamma * ((m_lambda * y - m_x[i]) - rho * (m_lambda * y + m_x[i])) / R1; + vr2 = -gamma * ((m_lambda * y - m_x[i]) + rho * (m_lambda * y + m_x[i])) / R2; + double vt = gamma * sigma * (y + m_lambda * m_x[i]); + vt1 = vt / R1; + vt2 = vt / R2; + for (auto j = 0lu; j < 3lu; ++j) { + m_v1[i][j] = vr1 * ir1[j] + vt1 * it1[j]; + } + for (auto j = 0lu; j < 3lu; ++j) { + m_v2[i][j] = vr2 * ir2[j] + vt2 * it2[j]; + } } - } } unsigned lambert_problem::householder(double T, double &x0, unsigned N, // NOLINT - double eps, unsigned iter_max) const { - unsigned it = 0; - double err = 1.0; - double xnew = 0.0; - double tof = 0.0, delta = 0.0, DT = 0.0, DDT = 0.0, DDDT = 0.0; - while ((err > eps) && (it < iter_max)) { - x2tof(tof, x0, N); - dTdx(DT, DDT, DDDT, x0, tof); - delta = tof - T; - double DT2 = DT * DT; - xnew = x0 - delta * (DT2 - delta * DDT / 2.0) / - (DT * (DT2 - delta * DDT) + DDDT * delta * delta / 6.0); - err = std::abs(x0 - xnew); - x0 = xnew; - it++; - } - return it; + double eps, unsigned iter_max) const +{ + unsigned it = 0; + double err = 1.0; + double xnew = 0.0; + double tof = 0.0, delta = 0.0, DT = 0.0, DDT = 0.0, DDDT = 0.0; + while ((err > eps) && (it < iter_max)) { + x2tof(tof, x0, N); + dTdx(DT, DDT, DDDT, x0, tof); + delta = tof - T; + double DT2 = DT * DT; + xnew = x0 - delta * (DT2 - delta * DDT / 2.0) / (DT * (DT2 - delta * DDT) + DDDT * delta * delta / 6.0); + err = std::abs(x0 - xnew); + x0 = xnew; + it++; + } + return it; } -void lambert_problem::dTdx(double &DT, double &DDT, double &DDDT, double x, - double T) const { - double l2 = m_lambda * m_lambda; - double l3 = l2 * m_lambda; - double umx2 = 1.0 - x * x; - double y = std::sqrt(1.0 - l2 * umx2); - double y2 = y * y; - double y3 = y2 * y; - DT = 1.0 / umx2 * (3.0 * T * x - 2.0 + 2.0 * l3 * x / y); - DDT = 1.0 / umx2 * (3.0 * T + 5.0 * x * DT + 2.0 * (1.0 - l2) * l3 / y3); - DDDT = 1.0 / umx2 * - (7.0 * x * DDT + 8.0 * DT - 6.0 * (1.0 - l2) * l2 * l3 * x / y3 / y2); +void lambert_problem::dTdx(double &DT, double &DDT, double &DDDT, double x, double T) const +{ + double l2 = m_lambda * m_lambda; + double l3 = l2 * m_lambda; + double umx2 = 1.0 - x * x; + double y = std::sqrt(1.0 - l2 * umx2); + double y2 = y * y; + double y3 = y2 * y; + DT = 1.0 / umx2 * (3.0 * T * x - 2.0 + 2.0 * l3 * x / y); + DDT = 1.0 / umx2 * (3.0 * T + 5.0 * x * DT + 2.0 * (1.0 - l2) * l3 / y3); + DDDT = 1.0 / umx2 * (7.0 * x * DDT + 8.0 * DT - 6.0 * (1.0 - l2) * l2 * l3 * x / y3 / y2); } void lambert_problem::x2tof2(double &tof, double x, // NOLINT - unsigned N) const { - double a = 1.0 / (1.0 - x * x); - if (a > 0) // ellipse - { - double alfa = 2.0 * std::acos(x); - double beta = 2.0 * std::asin(std::sqrt(m_lambda * m_lambda / a)); - if (m_lambda < 0.0) { - beta = -beta; - } - tof = ((a * std::sqrt(a) * - ((alfa - std::sin(alfa)) - (beta - std::sin(beta)) + - 2.0 * kep3::pi * N)) / - 2.0); - } else { - double alfa = 2.0 * std::acosh(x); - double beta = 2.0 * std::asinh(std::sqrt(-m_lambda * m_lambda / a)); - if (m_lambda < 0.0) { - beta = -beta; + unsigned N) const +{ + double a = 1.0 / (1.0 - x * x); + if (a > 0) // ellipse + { + double alfa = 2.0 * std::acos(x); + double beta = 2.0 * std::asin(std::sqrt(m_lambda * m_lambda / a)); + if (m_lambda < 0.0) { + beta = -beta; + } + tof = ((a * std::sqrt(a) * ((alfa - std::sin(alfa)) - (beta - std::sin(beta)) + 2.0 * kep3::pi * N)) / 2.0); + } else { + double alfa = 2.0 * std::acosh(x); + double beta = 2.0 * std::asinh(std::sqrt(-m_lambda * m_lambda / a)); + if (m_lambda < 0.0) { + beta = -beta; + } + tof = (-a * std::sqrt(-a) * ((beta - std::sinh(beta)) - (alfa - std::sinh(alfa))) / 2.0); } - tof = (-a * std::sqrt(-a) * - ((beta - std::sinh(beta)) - (alfa - std::sinh(alfa))) / 2.0); - } } -void lambert_problem::x2tof(double &tof, double x, unsigned N) const { - double battin = 0.01; - double lagrange = 0.2; - double dist = std::abs(x - 1); - if (dist < lagrange && dist > battin) { // We use Lagrange tof expression - x2tof2(tof, x, N); - return; - } - double K = m_lambda * m_lambda; - double E = x * x - 1.0; - double rho = std::abs(E); - double z = std::sqrt(1 + K * E); - if (dist < battin) { // We use Battin series tof expression - double eta = z - m_lambda * x; - double S1 = 0.5 * (1.0 - m_lambda - x * eta); - double Q = hypergeometricF(S1, 1e-11); - Q = 4.0 / 3.0 * Q; - tof = (eta * eta * eta * Q + 4.0 * m_lambda * eta) / 2.0 + - N * kep3::pi / std::pow(rho, 1.5); - return; - } else { // We use Lancaster tof expresion - double y = std::sqrt(rho); - double g = x * z - m_lambda * E; - double d = 0.0; - if (E < 0) { - double l = std::acos(g); - d = N * kep3::pi + l; - } else { - double f = y * (z - m_lambda * x); - d = std::log(f + g); +void lambert_problem::x2tof(double &tof, double x, unsigned N) const +{ + double battin = 0.01; + double lagrange = 0.2; + double dist = std::abs(x - 1); + if (dist < lagrange && dist > battin) { // We use Lagrange tof expression + x2tof2(tof, x, N); + return; + } + double K = m_lambda * m_lambda; + double E = x * x - 1.0; + double rho = std::abs(E); + double z = std::sqrt(1 + K * E); + if (dist < battin) { // We use Battin series tof expression + double eta = z - m_lambda * x; + double S1 = 0.5 * (1.0 - m_lambda - x * eta); + double Q = hypergeometricF(S1, 1e-11); + Q = 4.0 / 3.0 * Q; + tof = (eta * eta * eta * Q + 4.0 * m_lambda * eta) / 2.0 + N * kep3::pi / std::pow(rho, 1.5); + return; + } else { // We use Lancaster tof expresion + double y = std::sqrt(rho); + double g = x * z - m_lambda * E; + double d = 0.0; + if (E < 0) { + double l = std::acos(g); + d = N * kep3::pi + l; + } else { + double f = y * (z - m_lambda * x); + d = std::log(f + g); + } + tof = (x - m_lambda * z - d / y) / E; + return; } - tof = (x - m_lambda * z - d / y) / E; - return; - } } -double lambert_problem::hypergeometricF(double z, double tol) const { // NOLINT - double Sj = 1.0; - double Cj = 1.0; - double err = 1.0; - double Cj1 = 0.0; - double Sj1 = 0.0; - int j = 0; - while (err > tol) { - Cj1 = Cj * (3.0 + j) * (1.0 + j) / (2.5 + j) * z / (j + 1); - Sj1 = Sj + Cj1; - err = std::abs(Cj1); - Sj = Sj1; - Cj = Cj1; - j = j + 1; - } - return Sj; +double lambert_problem::hypergeometricF(double z, double tol) const +{ // NOLINT + double Sj = 1.0; + double Cj = 1.0; + double err = 1.0; + double Cj1 = 0.0; + double Sj1 = 0.0; + int j = 0; + while (err > tol) { + Cj1 = Cj * (3.0 + j) * (1.0 + j) / (2.5 + j) * z / (j + 1); + Sj1 = Sj + Cj1; + err = std::abs(Cj1); + Sj = Sj1; + Cj = Cj1; + j = j + 1; + } + return Sj; } /// Gets velocity at r1 @@ -309,8 +298,9 @@ double lambert_problem::hypergeometricF(double z, double tol) const { // NOLINT * \return an std::vector containing 3-d arrays with the cartesian components of * the velocities at r1 for all 2N_max+1 solutions */ -const std::vector> &lambert_problem::get_v1() const { - return m_v1; +const std::vector> &lambert_problem::get_v1() const +{ + return m_v1; } /// Gets velocity at r2 @@ -319,8 +309,9 @@ const std::vector> &lambert_problem::get_v1() const { * \return an std::vector containing 3-d arrays with the cartesian components of * the velocities at r2 for all 2N_max+1 solutions */ -const std::vector> &lambert_problem::get_v2() const { - return m_v2; +const std::vector> &lambert_problem::get_v2() const +{ + return m_v2; } /// Gets r1 @@ -328,21 +319,30 @@ const std::vector> &lambert_problem::get_v2() const { * * \return a 3-d array with the cartesian components of r1 */ -const std::array &lambert_problem::get_r1() const { return m_r1; } +const std::array &lambert_problem::get_r1() const +{ + return m_r1; +} /// Gets r2 /** * * \return a 3-d array with the cartesian components of r2 */ -const std::array &lambert_problem::get_r2() const { return m_r2; } +const std::array &lambert_problem::get_r2() const +{ + return m_r2; +} /// Gets the time of flight between r1 and r2 /** * * \return the time of flight */ -const double &lambert_problem::get_tof() const { return m_tof; } +const double &lambert_problem::get_tof() const +{ + return m_tof; +} /// Gets the x variable /** @@ -350,14 +350,20 @@ const double &lambert_problem::get_tof() const { return m_tof; } * * \return the x variables in an std::vector */ -const std::vector &lambert_problem::get_x() const { return m_x; } +const std::vector &lambert_problem::get_x() const +{ + return m_x; +} /// Gets gravitational parameter /** * * \return the gravitational parameter */ -const double &lambert_problem::get_mu() const { return m_mu; } +const double &lambert_problem::get_mu() const +{ + return m_mu; +} /// Gets number of iterations /** @@ -365,8 +371,9 @@ const double &lambert_problem::get_mu() const { return m_mu; } * \return an std::vector containing the iterations taken to compute each one of * the solutions */ -const std::vector &lambert_problem::get_iters() const { - return m_iters; +const std::vector &lambert_problem::get_iters() const +{ + return m_iters; } /// Gets N_max @@ -375,58 +382,51 @@ const std::vector &lambert_problem::get_iters() const { * \return the maximum number of revolutions. The number of solutions to the * problem will be Nmax*2 +1 */ -unsigned lambert_problem::get_Nmax() const { return m_Nmax; } +unsigned lambert_problem::get_Nmax() const +{ + return m_Nmax; +} /// Streaming operator -std::ostream &operator<<(std::ostream &s, const lambert_problem &lp) { - s << std::setprecision(16) << "Lambert's problem:" << std::endl; - s << "mu = " << lp.m_mu << std::endl; - s << "r1 = " - << "[" << lp.m_r1[0] << ", " << lp.m_r1[1] << ", " << lp.m_r1[2] << "]" - << std::endl; - s << "r2 = " - << "[" << lp.m_r2[0] << ", " << lp.m_r2[1] << ", " << lp.m_r2[2] << "]" - << std::endl; - s << "Time of flight: " << lp.m_tof << std::endl << std::endl; - s << "chord = " << lp.m_c << std::endl; - s << "semiperimeter = " << lp.m_s << std::endl; - s << "lambda = " << lp.m_lambda << std::endl; - s << "non dimensional time of flight = " - << lp.m_tof * sqrt(2 * lp.m_mu / lp.m_s / lp.m_s / lp.m_s) << std::endl - << std::endl; - s << "Maximum number of revolutions: " << lp.m_Nmax << std::endl; - s << "Solutions: " << std::endl; - s << "0 revs, Iters: " << lp.m_iters[0] << ", x: " << lp.m_x[0] - << ", a: " << lp.m_s / 2.0 / (1 - lp.m_x[0] * lp.m_x[0]) << std::endl; - s << "\tv1 = " - << "[" << lp.m_v1[0][0] << ", " << lp.m_v1[0][1] << ", " << lp.m_v1[0][2] - << "]"; - s << " v2 = " - << "[" << lp.m_v2[0][0] << ", " << lp.m_v2[0][1] << ", " << lp.m_v2[0][2] - << "]" << std::endl; - for (std::vector::size_type i = 0lu; i < lp.m_Nmax; ++i) { - s << i + 1 << " revs, left. Iters: " << lp.m_iters[1 + 2 * i] - << ", x: " << lp.m_x[1 + 2 * i] - << ", a: " << lp.m_s / 2.0 / (1 - lp.m_x[1 + 2 * i] * lp.m_x[1 + 2 * i]) +std::ostream &operator<<(std::ostream &s, const lambert_problem &lp) +{ + s << std::setprecision(16) << "Lambert's problem:" << std::endl; + s << "mu = " << lp.m_mu << std::endl; + s << "r1 = " + << "[" << lp.m_r1[0] << ", " << lp.m_r1[1] << ", " << lp.m_r1[2] << "]" << std::endl; + s << "r2 = " + << "[" << lp.m_r2[0] << ", " << lp.m_r2[1] << ", " << lp.m_r2[2] << "]" << std::endl; + s << "Time of flight: " << lp.m_tof << std::endl << std::endl; + s << "chord = " << lp.m_c << std::endl; + s << "semiperimeter = " << lp.m_s << std::endl; + s << "lambda = " << lp.m_lambda << std::endl; + s << "non dimensional time of flight = " << lp.m_tof * sqrt(2 * lp.m_mu / lp.m_s / lp.m_s / lp.m_s) << std::endl << std::endl; + s << "Maximum number of revolutions: " << lp.m_Nmax << std::endl; + s << "Solutions: " << std::endl; + s << "0 revs, Iters: " << lp.m_iters[0] << ", x: " << lp.m_x[0] + << ", a: " << lp.m_s / 2.0 / (1 - lp.m_x[0] * lp.m_x[0]) << std::endl; s << "\tv1 = " - << "[" << lp.m_v1[1 + 2 * i][0] << ", " << lp.m_v1[1 + 2 * i][1] << ", " - << lp.m_v1[1 + 2 * i][2] << "]"; + << "[" << lp.m_v1[0][0] << ", " << lp.m_v1[0][1] << ", " << lp.m_v1[0][2] << "]"; s << " v2 = " - << "[" << lp.m_v2[1 + 2 * i][0] << ", " << lp.m_v2[1 + 2 * i][1] << ", " - << lp.m_v2[1 + 2 * i][2] << "]" << std::endl; - s << i + 1 << " revs, right. Iters: " << lp.m_iters[2 + 2 * i] - << ", a: " << lp.m_x[2 + 2 * i] - << ", a: " << lp.m_s / 2.0 / (1 - lp.m_x[2 + 2 * i] * lp.m_x[2 + 2 * i]) - << std::endl; - s << "\tv1 = " - << "[" << lp.m_v1[2 + 2 * i][0] << ", " << lp.m_v1[2 + 2 * i][1] << ", " - << lp.m_v1[2 + 2 * i][2] << "]"; - s << " v2 = " - << "[" << lp.m_v2[2 + 2 * i][0] << ", " << lp.m_v2[2 + 2 * i][1] << ", " - << lp.m_v2[2 + 2 * i][2] << "]" << std::endl; - } - return s; + << "[" << lp.m_v2[0][0] << ", " << lp.m_v2[0][1] << ", " << lp.m_v2[0][2] << "]" << std::endl; + for (std::vector::size_type i = 0lu; i < lp.m_Nmax; ++i) { + s << i + 1 << " revs, left. Iters: " << lp.m_iters[1 + 2 * i] << ", x: " << lp.m_x[1 + 2 * i] + << ", a: " << lp.m_s / 2.0 / (1 - lp.m_x[1 + 2 * i] * lp.m_x[1 + 2 * i]) << std::endl; + s << "\tv1 = " + << "[" << lp.m_v1[1 + 2 * i][0] << ", " << lp.m_v1[1 + 2 * i][1] << ", " << lp.m_v1[1 + 2 * i][2] << "]"; + s << " v2 = " + << "[" << lp.m_v2[1 + 2 * i][0] << ", " << lp.m_v2[1 + 2 * i][1] << ", " << lp.m_v2[1 + 2 * i][2] << "]" + << std::endl; + s << i + 1 << " revs, right. Iters: " << lp.m_iters[2 + 2 * i] << ", a: " << lp.m_x[2 + 2 * i] + << ", a: " << lp.m_s / 2.0 / (1 - lp.m_x[2 + 2 * i] * lp.m_x[2 + 2 * i]) << std::endl; + s << "\tv1 = " + << "[" << lp.m_v1[2 + 2 * i][0] << ", " << lp.m_v1[2 + 2 * i][1] << ", " << lp.m_v1[2 + 2 * i][2] << "]"; + s << " v2 = " + << "[" << lp.m_v2[2 + 2 * i][0] << ", " << lp.m_v2[2 + 2 * i][1] << ", " << lp.m_v2[2 + 2 * i][2] << "]" + << std::endl; + } + return s; } } // namespace kep3 \ No newline at end of file diff --git a/src/planet.cpp b/src/planet.cpp index a121c379..01a0a107 100644 --- a/src/planet.cpp +++ b/src/planet.cpp @@ -10,104 +10,116 @@ #include #include -namespace kep3::detail { - -std::array, 2> null_udpla::eph(const epoch &) { - std::array pos = {1., 0., 0.}; - std::array vel = {0., 1., 0.}; - return {pos, vel}; +namespace kep3::detail +{ + +std::array, 2> null_udpla::eph(const epoch &) +{ + std::array pos = {1., 0., 0.}; + std::array vel = {0., 1., 0.}; + return {pos, vel}; }; } // namespace kep3::detail -namespace kep3 { +namespace kep3 +{ planet::planet() : planet(detail::null_udpla{}){}; planet::planet(const planet &other) : m_ptr(other.m_ptr->clone()){}; planet::planet(planet &&other) noexcept : m_ptr(std::move(other.m_ptr)){}; -planet &planet::operator=(planet &&other) noexcept { - if (this != &other) { - m_ptr = std::move(other.m_ptr); - } - return *this; +planet &planet::operator=(planet &&other) noexcept +{ + if (this != &other) { + m_ptr = std::move(other.m_ptr); + } + return *this; } -planet &planet::operator=(const planet &other) { return *this = planet(other); } +planet &planet::operator=(const planet &other) +{ + return *this = planet(other); +} -bool planet::is_valid() const { return static_cast(m_ptr); } +bool planet::is_valid() const +{ + return static_cast(m_ptr); +} -std::type_index planet::get_type_index() const { - return ptr()->get_type_index(); +std::type_index planet::get_type_index() const +{ + return ptr()->get_type_index(); } -const void *planet::get_ptr() const { return ptr()->get_ptr(); } +const void *planet::get_ptr() const +{ + return ptr()->get_ptr(); +} -void *planet::get_ptr() { return ptr()->get_ptr(); } +void *planet::get_ptr() +{ + return ptr()->get_ptr(); +} -detail::planet_inner_base const *planet::ptr() const { - assert(m_ptr.get() != nullptr); - return m_ptr.get(); +detail::planet_inner_base const *planet::ptr() const +{ + assert(m_ptr.get() != nullptr); + return m_ptr.get(); } -detail::planet_inner_base *planet::ptr() { - assert(m_ptr.get() != nullptr); - return m_ptr.get(); +detail::planet_inner_base *planet::ptr() +{ + assert(m_ptr.get() != nullptr); + return m_ptr.get(); } -std::array, 2> planet::eph(const epoch &ep) { - return ptr()->eph(ep); +std::array, 2> planet::eph(const epoch &ep) +{ + return ptr()->eph(ep); } -double planet::period(const epoch &ep) const { return ptr()->period(ep); } +double planet::period(const epoch &ep) const +{ + return ptr()->period(ep); +} -std::string planet::get_name() const { return ptr()->get_name(); } +std::string planet::get_name() const +{ + return ptr()->get_name(); +} -std::string planet::get_extra_info() const { return ptr()->get_extra_info(); } +std::string planet::get_extra_info() const +{ + return ptr()->get_extra_info(); +} -double planet::get_mu_central_body() const { - double mu = ptr()->get_mu_central_body(); - if (mu == -1.) { - throw not_implemented_error( - "A central body mu has not been declared for '" + get_name() + "'"); - } - return mu; +double planet::get_mu_central_body() const +{ + return ptr()->get_mu_central_body(); } -double planet::get_mu_self() const { - double mu = ptr()->get_mu_self(); - if (mu == -1.) { - throw not_implemented_error("A body mu has not been declared for '" + - get_name() + "'"); - } - return mu; +double planet::get_mu_self() const +{ + return ptr()->get_mu_self();; } -double planet::get_radius() const { - double mu = ptr()->get_radius(); - if (mu == -1.) { - throw not_implemented_error("A body radius has not been declared for '" + - get_name() + "'"); - } - return mu; +double planet::get_radius() const +{ + return ptr()->get_radius(); } -double planet::get_safe_radius() const { - double mu = ptr()->get_safe_radius(); - if (mu == -1.) { - throw not_implemented_error( - "A body safe radius has has not been declared for '" + get_name() + - "'"); - } - return mu; +double planet::get_safe_radius() const +{ + return ptr()->get_safe_radius();; } -std::ostream &operator<<(std::ostream &os, const planet &p) { - os << "Planet name: " << p.get_name(); - os << "\nC++ class name: " - << detail::demangle_from_typeid(p.get_type_index().name()) << '\n'; - const auto extra_str = p.get_extra_info(); - if (!extra_str.empty()) { - os << "\nExtra info:\n" << extra_str; - } - return os; +std::ostream &operator<<(std::ostream &os, const planet &p) +{ + os << "Planet name: " << p.get_name(); + os << "\nC++ class name: " << detail::demangle_from_typeid(p.get_type_index().name()) << '\n'; + const auto extra_str = p.get_extra_info(); + if (!extra_str.empty()) { + os << "\nExtra info:\n" << extra_str; + } + return os; } } // namespace kep3 \ No newline at end of file diff --git a/src/planets/jpl_lp.cpp b/src/planets/jpl_lp.cpp index 1ba83b31..cc870634 100644 --- a/src/planets/jpl_lp.cpp +++ b/src/planets/jpl_lp.cpp @@ -22,7 +22,8 @@ #include #include -namespace kep3::udpla { +namespace kep3::udpla +{ // Data from: https://ssd.jpl.nasa.gov/planets/approx_pos.html // a,e,i,L,W,w @@ -46,164 +47,179 @@ static const std::array neptune_el_dot = {0.00026291, 0.00005105, 0.0 // clang-format on // NOLINTNEXTLINE(cert-err58-cpp) -const std::unordered_map mapped_planets = { - {"mercury", 1}, {"venus", 2}, {"earth", 3}, {"mars", 4}, {"jupiter", 5}, - {"saturn", 6}, {"uranus", 7}, {"neptune", 8}, {"pluto", 9}}; +const std::unordered_map mapped_planets + = {{"mercury", 1}, {"venus", 2}, {"earth", 3}, {"mars", 4}, {"jupiter", 5}, + {"saturn", 6}, {"uranus", 7}, {"neptune", 8}, {"pluto", 9}}; jpl_lp::jpl_lp(std::string name) - : m_elements(), m_elements_dot(), m_name(std::move(name)), - m_mu_central_body(kep3::MU_SUN) { - - boost::algorithm::to_lower(m_name); - switch (mapped_planets.at(m_name)) { - case (1): { - m_elements = mercury_el; - m_elements_dot = mercury_el_dot; - m_radius = 2440000.; - m_safe_radius = 1.1 * m_radius; - m_mu_self = 22032e9; - } break; - case (2): { - m_elements = venus_el; - m_elements_dot = venus_el_dot; - m_radius = 6052000.; - m_safe_radius = 1.1 * m_radius; - m_mu_self = 324859e9; - } break; - case (3): { - m_elements = earth_moon_el; - m_elements_dot = earth_moon_el_dot; - m_radius = 6378000.; - m_safe_radius = 1.1 * m_radius; - m_mu_self = 398600.4418e9; - } break; - case (4): { - m_elements = mars_el; - m_elements_dot = mars_el_dot; - m_radius = 3397000.; - m_safe_radius = 1.1 * m_radius; - m_mu_self = 42828e9; - } break; - case (5): { - m_elements = jupiter_el; - m_elements_dot = jupiter_el_dot; - m_radius = 71492000.; - m_safe_radius = 9. * m_radius; - m_mu_self = 126686534e9; - } break; - case (6): { - m_elements = saturn_el; - m_elements_dot = saturn_el_dot; - m_radius = 60330000.; - m_safe_radius = 1.1 * m_radius; - m_mu_self = 37931187e9; - } break; - case (7): { - m_elements = uranus_el; - m_elements_dot = uranus_el_dot; - m_radius = 25362000.; - m_safe_radius = 1.1 * m_radius; - m_mu_self = 5793939e9; - } break; - case (8): { - m_elements = neptune_el; - m_elements_dot = neptune_el_dot; - m_radius = 24622000.; - m_safe_radius = 1.1 * m_radius; - m_mu_self = 6836529e9; - } break; - default: { - throw std::logic_error("unknown planet name: "); // LCOV_EXCL_LINE - } - } + : m_elements(), m_elements_dot(), m_name(std::move(name)), m_mu_central_body(kep3::MU_SUN) +{ + + boost::algorithm::to_lower(m_name); + switch (mapped_planets.at(m_name)) { + case (1): { + m_elements = mercury_el; + m_elements_dot = mercury_el_dot; + m_radius = 2440000.; + m_safe_radius = 1.1 * m_radius; + m_mu_self = 22032e9; + } break; + case (2): { + m_elements = venus_el; + m_elements_dot = venus_el_dot; + m_radius = 6052000.; + m_safe_radius = 1.1 * m_radius; + m_mu_self = 324859e9; + } break; + case (3): { + m_elements = earth_moon_el; + m_elements_dot = earth_moon_el_dot; + m_radius = 6378000.; + m_safe_radius = 1.1 * m_radius; + m_mu_self = 398600.4418e9; + } break; + case (4): { + m_elements = mars_el; + m_elements_dot = mars_el_dot; + m_radius = 3397000.; + m_safe_radius = 1.1 * m_radius; + m_mu_self = 42828e9; + } break; + case (5): { + m_elements = jupiter_el; + m_elements_dot = jupiter_el_dot; + m_radius = 71492000.; + m_safe_radius = 9. * m_radius; + m_mu_self = 126686534e9; + } break; + case (6): { + m_elements = saturn_el; + m_elements_dot = saturn_el_dot; + m_radius = 60330000.; + m_safe_radius = 1.1 * m_radius; + m_mu_self = 37931187e9; + } break; + case (7): { + m_elements = uranus_el; + m_elements_dot = uranus_el_dot; + m_radius = 25362000.; + m_safe_radius = 1.1 * m_radius; + m_mu_self = 5793939e9; + } break; + case (8): { + m_elements = neptune_el; + m_elements_dot = neptune_el_dot; + m_radius = 24622000.; + m_safe_radius = 1.1 * m_radius; + m_mu_self = 6836529e9; + } break; + default: { + throw std::logic_error("unknown planet name: "); // LCOV_EXCL_LINE + } + } } // Computes the kep3::KEP_F elements (osculating with true anomaly) at epoch. -std::array jpl_lp::_f_elements(const kep3::epoch &ep) const { - double mjd2000 = ep.mjd2000(); - if (mjd2000 <= -73048.0 || mjd2000 >= 18263.0) { - throw std::domain_error("Low precision Ephemeris are only valid in the " - "range range [1800-2050]"); - } - // algorithm from https://ssd.jpl.nasa.gov/planets/approx_pos.html accessed - // 2023. - std::array elements_updated{}, elements_f{}; - double dt = mjd2000 / 36525.; // Number of centuries passed since J2000.0 - for (unsigned int i = 0; i < 6; ++i) { - elements_updated[i] = (m_elements[i] + m_elements_dot[i] * dt); - } - elements_f[0] = elements_updated[0] * kep3::AU; - elements_f[1] = elements_updated[1]; - elements_f[2] = elements_updated[2] * kep3::DEG2RAD; - elements_f[3] = elements_updated[5] * kep3::DEG2RAD; - elements_f[4] = (elements_updated[4] - elements_updated[5]) * kep3::DEG2RAD; - elements_f[5] = (elements_updated[3] - elements_updated[4]) * kep3::DEG2RAD; - elements_f[5] = kep3::m2f(elements_f[5], elements_f[1]); - return elements_f; +std::array jpl_lp::_f_elements(const kep3::epoch &ep) const +{ + double mjd2000 = ep.mjd2000(); + if (mjd2000 <= -73048.0 || mjd2000 >= 18263.0) { + throw std::domain_error("Low precision Ephemeris are only valid in the " + "range range [1800-2050]"); + } + // algorithm from https://ssd.jpl.nasa.gov/planets/approx_pos.html accessed + // 2023. + std::array elements_updated{}, elements_f{}; + double dt = mjd2000 / 36525.; // Number of centuries passed since J2000.0 + for (unsigned int i = 0; i < 6; ++i) { + elements_updated[i] = (m_elements[i] + m_elements_dot[i] * dt); + } + elements_f[0] = elements_updated[0] * kep3::AU; + elements_f[1] = elements_updated[1]; + elements_f[2] = elements_updated[2] * kep3::DEG2RAD; + elements_f[3] = elements_updated[5] * kep3::DEG2RAD; + elements_f[4] = (elements_updated[4] - elements_updated[5]) * kep3::DEG2RAD; + elements_f[5] = (elements_updated[3] - elements_updated[4]) * kep3::DEG2RAD; + elements_f[5] = kep3::m2f(elements_f[5], elements_f[1]); + return elements_f; } -std::array, 2> jpl_lp::eph(const epoch &ep) const { - auto elements_f = _f_elements(ep); - return par2ic(elements_f, get_mu_central_body()); +std::array, 2> jpl_lp::eph(const epoch &ep) const +{ + auto elements_f = _f_elements(ep); + return par2ic(elements_f, get_mu_central_body()); } -std::array jpl_lp::elements(const kep3::epoch &ep, - kep3::elements_type el_type) const { - auto elements = _f_elements(ep); - switch (el_type) { - case kep3::elements_type::KEP_F: - break; - case kep3::elements_type::KEP_M: - elements[5] = kep3::f2m(elements[5], elements[1]); - break; - case kep3::elements_type::MEQ: - elements = kep3::par2eq(elements, false); - break; - case kep3::elements_type::MEQ_R: - elements = kep3::par2eq(elements, true); - break; - default: - throw std::logic_error("You should not go here!"); - } - return elements; +std::array jpl_lp::elements(const kep3::epoch &ep, kep3::elements_type el_type) const +{ + auto elements = _f_elements(ep); + switch (el_type) { + case kep3::elements_type::KEP_F: + break; + case kep3::elements_type::KEP_M: + elements[5] = kep3::f2m(elements[5], elements[1]); + break; + case kep3::elements_type::MEQ: + elements = kep3::par2eq(elements, false); + break; + case kep3::elements_type::MEQ_R: + elements = kep3::par2eq(elements, true); + break; + default: + throw std::logic_error("You should not go here!"); + } + return elements; } -std::string jpl_lp::get_name() const { return m_name; } - -double jpl_lp::get_mu_central_body() const { return m_mu_central_body; } - -double jpl_lp::get_mu_self() const { return m_mu_self; } - -double jpl_lp::get_radius() const { return m_radius; } - -double jpl_lp::get_safe_radius() const { return m_safe_radius; } - -std::string jpl_lp::get_extra_info() const { - kep3::epoch ep{0., kep3::epoch::julian_type::MJD2000}; - auto par = elements(ep); - auto pos_vel = eph(ep); - - std::string retval = - fmt::format("\nLow-precision planet elements: \n") + - fmt::format("Semi major axis (AU): {}\n", par[0] / kep3::AU) + - fmt::format("Eccentricity: {}\n", par[1]) + - fmt::format("Inclination (deg.): {}\n", par[2] * kep3::RAD2DEG) + - fmt::format("Big Omega (deg.): {}\n", par[3] * kep3::RAD2DEG) + - fmt::format("Small omega (deg.): {}\n", par[4] * kep3::RAD2DEG) + - fmt::format("True anomly (deg.): {}\n", par[5] * kep3::RAD2DEG); - retval += fmt::format("Mean anomly (deg.): {}\n", - kep3::f2m(par[5], par[1]) * kep3::RAD2DEG); - retval += - fmt::format("Elements reference epoch (MJD2000): {}\n", ep.mjd2000()) + - fmt::format("Elements reference epoch (date): {}\n", ep) + - fmt::format("r at ref. = {}\n", pos_vel[0]) + - fmt::format("v at ref. = {}\n", pos_vel[1]); - return retval; +std::string jpl_lp::get_name() const +{ + return m_name; } -std::ostream &operator<<(std::ostream &os, const kep3::udpla::jpl_lp &udpla) { - os << udpla.get_extra_info() << std::endl; - return os; +double jpl_lp::get_mu_central_body() const +{ + return m_mu_central_body; +} + +double jpl_lp::get_mu_self() const +{ + return m_mu_self; +} + +double jpl_lp::get_radius() const +{ + return m_radius; +} + +double jpl_lp::get_safe_radius() const +{ + return m_safe_radius; +} + +std::string jpl_lp::get_extra_info() const +{ + kep3::epoch ep{0., kep3::epoch::julian_type::MJD2000}; + auto par = elements(ep); + auto pos_vel = eph(ep); + + std::string retval = fmt::format("\nLow-precision planet elements: \n") + + fmt::format("Semi major axis (AU): {}\n", par[0] / kep3::AU) + + fmt::format("Eccentricity: {}\n", par[1]) + + fmt::format("Inclination (deg.): {}\n", par[2] * kep3::RAD2DEG) + + fmt::format("Big Omega (deg.): {}\n", par[3] * kep3::RAD2DEG) + + fmt::format("Small omega (deg.): {}\n", par[4] * kep3::RAD2DEG) + + fmt::format("True anomly (deg.): {}\n", par[5] * kep3::RAD2DEG); + retval += fmt::format("Mean anomly (deg.): {}\n", kep3::f2m(par[5], par[1]) * kep3::RAD2DEG); + retval += fmt::format("Elements reference epoch (MJD2000): {}\n", ep.mjd2000()) + + fmt::format("Elements reference epoch (date): {}\n", ep) + fmt::format("r at ref. = {}\n", pos_vel[0]) + + fmt::format("v at ref. = {}\n", pos_vel[1]); + return retval; +} + +std::ostream &operator<<(std::ostream &os, const kep3::udpla::jpl_lp &udpla) +{ + os << udpla.get_extra_info() << std::endl; + return os; } } // namespace kep3::udpla diff --git a/src/planets/keplerian.cpp b/src/planets/keplerian.cpp index e905923c..11deff5a 100644 --- a/src/planets/keplerian.cpp +++ b/src/planets/keplerian.cpp @@ -27,156 +27,162 @@ #include #include -namespace kep3::udpla { - -keplerian::keplerian(const epoch &ref_epoch, - const std::array, 2> &pos_vel, - double mu_central_body, std::string name, - std::array added_params) - : m_ref_epoch(ref_epoch), m_name(std::move(name)), - m_mu_central_body(mu_central_body), m_mu_self(added_params[0]), - m_radius(added_params[1]), m_safe_radius(added_params[2]), m_period(), - m_ellipse(), m_pos_vel_0(pos_vel) { - double R = - std::sqrt(pos_vel[0][0] * pos_vel[0][0] + pos_vel[0][1] * pos_vel[0][1] + - pos_vel[0][2] * pos_vel[0][2]); - double en = (pos_vel[1][0] * pos_vel[1][0] + pos_vel[1][1] * pos_vel[1][1] + - pos_vel[1][2] * pos_vel[1][2]) / - 2. - - mu_central_body / R; - (en > 0) ? m_ellipse = false : m_ellipse = true; - double a = -m_mu_central_body / 2. / en; - if (m_ellipse) { - m_period = kep3::pi * 2. * std::sqrt(a * a * a / m_mu_central_body); - } else { - m_period = std::numeric_limits::quiet_NaN(); - } +namespace kep3::udpla +{ + +keplerian::keplerian(const epoch &ref_epoch, const std::array, 2> &pos_vel, + double mu_central_body, std::string name, std::array added_params) + : m_ref_epoch(ref_epoch), m_name(std::move(name)), m_mu_central_body(mu_central_body), m_mu_self(added_params[0]), + m_radius(added_params[1]), m_safe_radius(added_params[2]), m_period(), m_ellipse(), m_pos_vel_0(pos_vel) +{ + double R = std::sqrt(pos_vel[0][0] * pos_vel[0][0] + pos_vel[0][1] * pos_vel[0][1] + pos_vel[0][2] * pos_vel[0][2]); + double en = (pos_vel[1][0] * pos_vel[1][0] + pos_vel[1][1] * pos_vel[1][1] + pos_vel[1][2] * pos_vel[1][2]) / 2. + - mu_central_body / R; + (en > 0) ? m_ellipse = false : m_ellipse = true; + double a = -m_mu_central_body / 2. / en; + if (m_ellipse) { + m_period = kep3::pi * 2. * std::sqrt(a * a * a / m_mu_central_body); + } else { + m_period = std::numeric_limits::quiet_NaN(); + } } -keplerian::keplerian(const epoch &ref_epoch, - const std::array &par_in, - double mu_central_body, std::string name, - std::array added_params, - kep3::elements_type el_type) - : m_ref_epoch(ref_epoch), m_name(std::move(name)), - m_mu_central_body(mu_central_body), m_mu_self(added_params[0]), - m_radius(added_params[1]), m_safe_radius(added_params[2]), m_period(), - m_ellipse(), m_pos_vel_0() { - // orbital parameters a,e,i,W,w,f will be stored here - std::array par(par_in); - // we convert according to the chosen input - switch (el_type) { - case kep3::elements_type::KEP_F: - break; - case kep3::elements_type::KEP_M: - if (par[0] < 0) { - throw std::logic_error("Mean anomaly is only available for ellipses."); +keplerian::keplerian(const epoch &ref_epoch, const std::array &par_in, double mu_central_body, + std::string name, std::array added_params, kep3::elements_type el_type) + : m_ref_epoch(ref_epoch), m_name(std::move(name)), m_mu_central_body(mu_central_body), m_mu_self(added_params[0]), + m_radius(added_params[1]), m_safe_radius(added_params[2]), m_period(), m_ellipse(), m_pos_vel_0() +{ + // orbital parameters a,e,i,W,w,f will be stored here + std::array par(par_in); + // we convert according to the chosen input + switch (el_type) { + case kep3::elements_type::KEP_F: + break; + case kep3::elements_type::KEP_M: + if (par[0] < 0) { + throw std::logic_error("Mean anomaly is only available for ellipses."); + } + par[5] = kep3::m2f(par[5], par[1]); + break; + case kep3::elements_type::MEQ: + par = kep3::eq2par(par, false); + break; + case kep3::elements_type::MEQ_R: + par = kep3::eq2par(par, true); + break; + default: + throw std::logic_error("You should not go here!"); + } + + if (par[0] * (1 - par[1]) <= 0) { + throw std::domain_error("A Keplerian planet constructor was called with with non compatible " + "a,e:" + "The following must hold: a<0 -> e>1 [a>0 -> e<1]."); + } + + m_pos_vel_0 = kep3::par2ic(par, mu_central_body); + + (par[0] < 0) ? m_ellipse = false : m_ellipse = true; + if (m_ellipse) { + m_period = kep3::pi * 2. * std::sqrt(par[0] * par[0] * par[0] / m_mu_central_body); + } else { + m_period = std::numeric_limits::quiet_NaN(); } - par[5] = kep3::m2f(par[5], par[1]); - break; - case kep3::elements_type::MEQ: - par = kep3::eq2par(par, false); - break; - case kep3::elements_type::MEQ_R: - par = kep3::eq2par(par, true); - break; - default: - throw std::logic_error("You should not go here!"); - } - - if (par[0] * (1 - par[1]) <= 0) { - throw std::domain_error( - "A Keplerian planet constructor was called with with non compatible " - "a,e:" - "The following must hold: a<0 -> e>1 [a>0 -> e<1]."); - } - - m_pos_vel_0 = kep3::par2ic(par, mu_central_body); - - (par[0] < 0) ? m_ellipse = false : m_ellipse = true; - if (m_ellipse) { - m_period = - kep3::pi * 2. * std::sqrt(par[0] * par[0] * par[0] / m_mu_central_body); - } else { - m_period = std::numeric_limits::quiet_NaN(); - } } -std::array, 2> -keplerian::eph(const kep3::epoch &ep) const { - // 1 - We compute the dt - auto dt = epoch::as_sec(ep - m_ref_epoch); - // 2 - We propagate (make a copy as we do not want to change m_pos_vel_0) - auto retval(m_pos_vel_0); - kep3::propagate_lagrangian(retval, dt, m_mu_central_body); - return retval; +std::array, 2> keplerian::eph(const kep3::epoch &ep) const +{ + // 1 - We compute the dt + auto dt = epoch::as_sec(ep - m_ref_epoch); + // 2 - We propagate (make a copy as we do not want to change m_pos_vel_0) + auto retval(m_pos_vel_0); + kep3::propagate_lagrangian(retval, dt, m_mu_central_body); + return retval; } -std::string keplerian::get_name() const { return m_name; } +std::string keplerian::get_name() const +{ + return m_name; +} -double keplerian::get_mu_central_body() const { return m_mu_central_body; } +double keplerian::get_mu_central_body() const +{ + return m_mu_central_body; +} -double keplerian::get_mu_self() const { return m_mu_self; } +double keplerian::get_mu_self() const +{ + return m_mu_self; +} -double keplerian::get_radius() const { return m_radius; } +double keplerian::get_radius() const +{ + return m_radius; +} -double keplerian::get_safe_radius() const { return m_safe_radius; } +double keplerian::get_safe_radius() const +{ + return m_safe_radius; +} -double keplerian::period(const kep3::epoch &) const { return m_period; } +double keplerian::period(const kep3::epoch &) const +{ + return m_period; +} -kep3::epoch keplerian::get_ref_epoch() const { return m_ref_epoch; } +kep3::epoch keplerian::get_ref_epoch() const +{ + return m_ref_epoch; +} -std::array keplerian::elements(kep3::elements_type el_type) const { - std::array retval{}; - switch (el_type) { - case kep3::elements_type::KEP_F: - retval = kep3::ic2par(m_pos_vel_0, m_mu_central_body); - break; - case kep3::elements_type::KEP_M: - if (!m_ellipse) { - throw std::logic_error("Mean anomaly is only available for ellipses."); +std::array keplerian::elements(kep3::elements_type el_type) const +{ + std::array retval{}; + switch (el_type) { + case kep3::elements_type::KEP_F: + retval = kep3::ic2par(m_pos_vel_0, m_mu_central_body); + break; + case kep3::elements_type::KEP_M: + if (!m_ellipse) { + throw std::logic_error("Mean anomaly is only available for ellipses."); + } + retval = kep3::ic2par(m_pos_vel_0, m_mu_central_body); + retval[5] = kep3::f2m(retval[5], retval[1]); + break; + case kep3::elements_type::MEQ: + retval = kep3::ic2eq(m_pos_vel_0, m_mu_central_body); + break; + case kep3::elements_type::MEQ_R: + retval = kep3::ic2eq(m_pos_vel_0, m_mu_central_body, true); + break; + default: + throw std::logic_error("You should not go here!"); } - retval = kep3::ic2par(m_pos_vel_0, m_mu_central_body); - retval[5] = kep3::f2m(retval[5], retval[1]); - break; - case kep3::elements_type::MEQ: - retval = kep3::ic2eq(m_pos_vel_0, m_mu_central_body); - break; - case kep3::elements_type::MEQ_R: - retval = kep3::ic2eq(m_pos_vel_0, m_mu_central_body, true); - break; - default: - throw std::logic_error("You should not go here!"); - } - return retval; + return retval; } -std::string keplerian::get_extra_info() const { - auto par = elements(); - std::string retval = - fmt::format("Keplerian planet elements: \n") + - fmt::format("Semi major axis (AU): {}\n", par[0] / kep3::AU) + - fmt::format("Eccentricity: {}\n", par[1]) + - fmt::format("Inclination (deg.): {}\n", par[2] * kep3::RAD2DEG) + - fmt::format("Big Omega (deg.): {}\n", par[3] * kep3::RAD2DEG) + - fmt::format("Small omega (deg.): {}\n", par[4] * kep3::RAD2DEG) + - fmt::format("True anomly (deg.): {}\n", par[5] * kep3::RAD2DEG); - if (m_ellipse) { - retval += fmt::format("Mean anomly (deg.): {}\n", - kep3::f2m(par[5], par[1]) * kep3::RAD2DEG); - } - retval += - fmt::format("Elements reference epoch (MJD2000): {}\n", m_ref_epoch) + - fmt::format("Elements reference epoch (date): {}\n", m_ref_epoch) + - fmt::format("r at ref. = {}\n", m_pos_vel_0[0]) + - fmt::format("v at ref. = {}\n", m_pos_vel_0[1]); - return retval; +std::string keplerian::get_extra_info() const +{ + auto par = elements(); + std::string retval + = fmt::format("Keplerian planet elements: \n") + fmt::format("Semi major axis (AU): {}\n", par[0] / kep3::AU) + + fmt::format("Eccentricity: {}\n", par[1]) + fmt::format("Inclination (deg.): {}\n", par[2] * kep3::RAD2DEG) + + fmt::format("Big Omega (deg.): {}\n", par[3] * kep3::RAD2DEG) + + fmt::format("Small omega (deg.): {}\n", par[4] * kep3::RAD2DEG) + + fmt::format("True anomly (deg.): {}\n", par[5] * kep3::RAD2DEG); + if (m_ellipse) { + retval += fmt::format("Mean anomly (deg.): {}\n", kep3::f2m(par[5], par[1]) * kep3::RAD2DEG); + } + retval += fmt::format("Elements reference epoch (MJD2000): {}\n", m_ref_epoch) + + fmt::format("Elements reference epoch (date): {}\n", m_ref_epoch) + + fmt::format("r at ref. = {}\n", m_pos_vel_0[0]) + fmt::format("v at ref. = {}\n", m_pos_vel_0[1]); + return retval; } -std::ostream &operator<<(std::ostream &os, - const kep3::udpla::keplerian &udpla) { - os << udpla.get_extra_info() << std::endl; - return os; +std::ostream &operator<<(std::ostream &os, const kep3::udpla::keplerian &udpla) +{ + os << udpla.get_extra_info() << std::endl; + return os; } } // namespace kep3::udpla diff --git a/test/catch.hpp b/test/catch.hpp index 061ae523..f227cfd7 100644 --- a/test/catch.hpp +++ b/test/catch.hpp @@ -19,49 +19,49 @@ #define CATCH_VERSION_PATCH 8 #ifdef __clang__ -# pragma clang system_header +#pragma clang system_header #elif defined __GNUC__ -# pragma GCC system_header +#pragma GCC system_header #endif // start catch_suppress_warnings.h #ifdef __clang__ -# ifdef __ICC // icpc defines the __clang__ macro -# pragma warning(push) -# pragma warning(disable: 161 1682) -# else // __ICC -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wpadded" -# pragma clang diagnostic ignored "-Wswitch-enum" -# pragma clang diagnostic ignored "-Wcovered-switch-default" -# endif +#ifdef __ICC // icpc defines the __clang__ macro +#pragma warning(push) +#pragma warning(disable : 161 1682) +#else // __ICC +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#pragma clang diagnostic ignored "-Wswitch-enum" +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#endif #elif defined __GNUC__ - // Because REQUIREs trigger GCC's -Wparentheses, and because still - // supported version of g++ have only buggy support for _Pragmas, - // Wparentheses have to be suppressed globally. -# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details - -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wunused-variable" -# pragma GCC diagnostic ignored "-Wpadded" +// Because REQUIREs trigger GCC's -Wparentheses, and because still +// supported version of g++ have only buggy support for _Pragmas, +// Wparentheses have to be suppressed globally. +#pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wpadded" #endif // end catch_suppress_warnings.h #if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) -# define CATCH_IMPL -# define CATCH_CONFIG_ALL_PARTS +#define CATCH_IMPL +#define CATCH_CONFIG_ALL_PARTS #endif // In the impl file, we want to have access to all parts of the headers // Can also be used to sanely support PCHs #if defined(CATCH_CONFIG_ALL_PARTS) -# define CATCH_CONFIG_EXTERNAL_INTERFACES -# if defined(CATCH_CONFIG_DISABLE_MATCHERS) -# undef CATCH_CONFIG_DISABLE_MATCHERS -# endif -# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) -# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER -# endif +#define CATCH_CONFIG_EXTERNAL_INTERFACES +#if defined(CATCH_CONFIG_DISABLE_MATCHERS) +#undef CATCH_CONFIG_DISABLE_MATCHERS +#endif +#if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +#define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +#endif #endif #if !defined(CATCH_CONFIG_IMPL_ONLY) @@ -70,34 +70,34 @@ // See e.g.: // https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html #ifdef __APPLE__ -# include -# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ - (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) -# define CATCH_PLATFORM_MAC -# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) -# define CATCH_PLATFORM_IPHONE -# endif +#include +#if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) +#define CATCH_PLATFORM_MAC +#elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) +#define CATCH_PLATFORM_IPHONE +#endif #elif defined(linux) || defined(__linux) || defined(__linux__) -# define CATCH_PLATFORM_LINUX +#define CATCH_PLATFORM_LINUX #elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) -# define CATCH_PLATFORM_WINDOWS +#define CATCH_PLATFORM_WINDOWS #endif // end catch_platform.h #ifdef CATCH_IMPL -# ifndef CLARA_CONFIG_MAIN -# define CLARA_CONFIG_MAIN_NOT_DEFINED -# define CLARA_CONFIG_MAIN -# endif +#ifndef CLARA_CONFIG_MAIN +#define CLARA_CONFIG_MAIN_NOT_DEFINED +#define CLARA_CONFIG_MAIN +#endif #endif // start catch_user_interfaces.h -namespace Catch { - unsigned int rngSeed(); +namespace Catch +{ +unsigned int rngSeed(); } // end catch_user_interfaces.h @@ -126,30 +126,30 @@ namespace Catch { #ifdef __cplusplus -# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) -# define CATCH_CPP14_OR_GREATER -# endif +#if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) +#define CATCH_CPP14_OR_GREATER +#endif -# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) -# define CATCH_CPP17_OR_GREATER -# endif +#if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +#define CATCH_CPP17_OR_GREATER +#endif #endif // Only GCC compiler should be used in this block, so other compilers trying to // mask themselves as GCC should be ignored. #if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) +#define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma("GCC diagnostic push") +#define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma("GCC diagnostic pop") -# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) +#define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) #endif #if defined(__clang__) -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) +#define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma("clang diagnostic push") +#define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma("clang diagnostic pop") // As of this writing, IBM XL's implementation of __builtin_constant_p has a bug // which results in calls to destructors being emitted for each temporary, @@ -162,62 +162,60 @@ namespace Catch { // ``` // // Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. -# if !defined(__ibmxl__) && !defined(__CUDACC__) -# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ -# endif +#if !defined(__ibmxl__) && !defined(__CUDACC__) +#define CATCH_INTERNAL_IGNORE_BUT_WARN(...) \ + (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ +#endif -# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ - _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") +#define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma("clang diagnostic ignored \"-Wexit-time-destructors\"") \ + _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) +#define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma("clang diagnostic ignored \"-Wparentheses\"") -# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) +#define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS _Pragma("clang diagnostic ignored \"-Wunused-variable\"") -# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) +#define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + _Pragma("clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"") -# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) +#define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS _Pragma("clang diagnostic ignored \"-Wunused-template\"") #endif // __clang__ //////////////////////////////////////////////////////////////////////////////// // Assume that non-Windows platforms support posix signals by default #if !defined(CATCH_PLATFORM_WINDOWS) - #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS +#define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS #endif //////////////////////////////////////////////////////////////////////////////// // We know some environments not to support full POSIX signals #if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) - #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +#define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS #endif #ifdef __OS400__ -# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS -# define CATCH_CONFIG_COLOUR_NONE +#define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +#define CATCH_CONFIG_COLOUR_NONE #endif //////////////////////////////////////////////////////////////////////////////// // Android somehow still does not support std::to_string #if defined(__ANDROID__) -# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING -# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE +#define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +#define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE #endif //////////////////////////////////////////////////////////////////////////////// // Not all Windows environments support SEH properly #if defined(__MINGW32__) -# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH #endif //////////////////////////////////////////////////////////////////////////////// // PS4 #if defined(__ORBIS__) -# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE +#define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE #endif //////////////////////////////////////////////////////////////////////////////// @@ -226,15 +224,14 @@ namespace Catch { // Required for some versions of Cygwin to declare gettimeofday // see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin -# define _BSD_SOURCE +#define _BSD_SOURCE // some versions of cygwin (most) do not support std::to_string. Use the libstd check. // https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 -# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ - && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) +#if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) -# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +#define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING -# endif +#endif #endif // __CYGWIN__ //////////////////////////////////////////////////////////////////////////////// @@ -243,49 +240,49 @@ namespace Catch { // Universal Windows platform does not support SEH // Or console colours (or console at all...) -# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) -# define CATCH_CONFIG_COLOUR_NONE -# else -# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH -# endif +#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +#define CATCH_CONFIG_COLOUR_NONE +#else +#define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +#endif -# if !defined(__clang__) // Handle Clang masquerading for msvc +#if !defined(__clang__) // Handle Clang masquerading for msvc // MSVC traditional preprocessor needs some workaround for __VA_ARGS__ // _MSVC_TRADITIONAL == 0 means new conformant preprocessor // _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor -# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) -# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -# endif // MSVC_TRADITIONAL +#if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +#define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#endif // MSVC_TRADITIONAL // Only do this if we're not using clang on Windows, which uses `diagnostic push` & `diagnostic pop` -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) -# endif // __clang__ +#define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma(warning(push)) +#define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma(warning(pop)) +#endif // __clang__ #endif // _MSC_VER #if defined(_REENTRANT) || defined(_MSC_VER) // Enable async processing, as -pthread is specified or no additional linking is required -# define CATCH_INTERNAL_CONFIG_USE_ASYNC +#define CATCH_INTERNAL_CONFIG_USE_ASYNC #endif // _MSC_VER //////////////////////////////////////////////////////////////////////////////// // Check if we are compiled with -fno-exceptions or equivalent #if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) -# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED +#define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED #endif //////////////////////////////////////////////////////////////////////////////// // DJGPP #ifdef __DJGPP__ -# define CATCH_INTERNAL_CONFIG_NO_WCHAR +#define CATCH_INTERNAL_CONFIG_NO_WCHAR #endif // __DJGPP__ //////////////////////////////////////////////////////////////////////////////// // Embarcadero C++Build #if defined(__BORLANDC__) - #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN +#define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN #endif //////////////////////////////////////////////////////////////////////////////// @@ -295,8 +292,8 @@ namespace Catch { // handled by it. // Otherwise all supported compilers support COUNTER macro, // but user still might want to turn it off -#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) - #define CATCH_INTERNAL_CONFIG_COUNTER +#if (!defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L) +#define CATCH_INTERNAL_CONFIG_COUNTER #endif //////////////////////////////////////////////////////////////////////////////// @@ -305,9 +302,9 @@ namespace Catch { // This means that it is detected as Windows, but does not provide // the same set of capabilities as real Windows does. #if defined(UNDER_RTSS) || defined(RTX64_BUILD) - #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH - #define CATCH_INTERNAL_CONFIG_NO_ASYNC - #define CATCH_CONFIG_COLOUR_NONE +#define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#define CATCH_INTERNAL_CONFIG_NO_ASYNC +#define CATCH_CONFIG_COLOUR_NONE #endif #if !defined(_GLIBCXX_USE_C99_MATH_TR1) @@ -316,139 +313,151 @@ namespace Catch { // Various stdlib support checks that require __has_include #if defined(__has_include) - // Check if string_view is available and usable - #if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW - #endif - - // Check if optional is available and usable - # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL - # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) - - // Check if byte is available and usable - # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # include - # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0) - # define CATCH_INTERNAL_CONFIG_CPP17_BYTE - # endif - # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) - - // Check if variant is available and usable - # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # if defined(__clang__) && (__clang_major__ < 8) - // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 - // fix should be in clang 8, workaround in libstdc++ 8.2 - # include - # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) - # define CATCH_CONFIG_NO_CPP17_VARIANT - # else - # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT - # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) - # else - # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT - # endif // defined(__clang__) && (__clang_major__ < 8) - # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +// Check if string_view is available and usable +#if __has_include() && defined(CATCH_CPP17_OR_GREATER) +#define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW +#endif + +// Check if optional is available and usable +#if __has_include() && defined(CATCH_CPP17_OR_GREATER) +#define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL +#endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + +// Check if byte is available and usable +#if __has_include() && defined(CATCH_CPP17_OR_GREATER) +#include +#if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0) +#define CATCH_INTERNAL_CONFIG_CPP17_BYTE +#endif +#endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + +// Check if variant is available and usable +#if __has_include() && defined(CATCH_CPP17_OR_GREATER) +#if defined(__clang__) && (__clang_major__ < 8) +// work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 +// fix should be in clang 8, workaround in libstdc++ 8.2 +#include +#if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) +#define CATCH_CONFIG_NO_CPP17_VARIANT +#else +#define CATCH_INTERNAL_CONFIG_CPP17_VARIANT +#endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) +#else +#define CATCH_INTERNAL_CONFIG_CPP17_VARIANT +#endif // defined(__clang__) && (__clang_major__ < 8) +#endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) #endif // defined(__has_include) #if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) -# define CATCH_CONFIG_COUNTER +#define CATCH_CONFIG_COUNTER #endif -#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) -# define CATCH_CONFIG_WINDOWS_SEH +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) \ + && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) +#define CATCH_CONFIG_WINDOWS_SEH #endif // This is set by default, because we assume that unix compilers are posix-signal-compatible by default. -#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) -# define CATCH_CONFIG_POSIX_SIGNALS +#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) \ + && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +#define CATCH_CONFIG_POSIX_SIGNALS #endif // This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. #if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) -# define CATCH_CONFIG_WCHAR +#define CATCH_CONFIG_WCHAR #endif -#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) -# define CATCH_CONFIG_CPP11_TO_STRING +#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) \ + && !defined(CATCH_CONFIG_CPP11_TO_STRING) +#define CATCH_CONFIG_CPP11_TO_STRING #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) -# define CATCH_CONFIG_CPP17_OPTIONAL +#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) \ + && !defined(CATCH_CONFIG_CPP17_OPTIONAL) +#define CATCH_CONFIG_CPP17_OPTIONAL #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) -# define CATCH_CONFIG_CPP17_STRING_VIEW +#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) \ + && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) +#define CATCH_CONFIG_CPP17_STRING_VIEW #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) -# define CATCH_CONFIG_CPP17_VARIANT +#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) \ + && !defined(CATCH_CONFIG_CPP17_VARIANT) +#define CATCH_CONFIG_CPP17_VARIANT #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) -# define CATCH_CONFIG_CPP17_BYTE +#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) \ + && !defined(CATCH_CONFIG_CPP17_BYTE) +#define CATCH_CONFIG_CPP17_BYTE #endif #if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) -# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE +#define CATCH_INTERNAL_CONFIG_NEW_CAPTURE #endif -#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) -# define CATCH_CONFIG_NEW_CAPTURE +#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) \ + && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) +#define CATCH_CONFIG_NEW_CAPTURE #endif #if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) -# define CATCH_CONFIG_DISABLE_EXCEPTIONS +#define CATCH_CONFIG_DISABLE_EXCEPTIONS #endif -#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) -# define CATCH_CONFIG_POLYFILL_ISNAN +#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) \ + && !defined(CATCH_CONFIG_POLYFILL_ISNAN) +#define CATCH_CONFIG_POLYFILL_ISNAN #endif -#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) -# define CATCH_CONFIG_USE_ASYNC +#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) \ + && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) +#define CATCH_CONFIG_USE_ASYNC #endif -#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) -# define CATCH_CONFIG_ANDROID_LOGWRITE +#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) \ + && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) +#define CATCH_CONFIG_ANDROID_LOGWRITE #endif -#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) -# define CATCH_CONFIG_GLOBAL_NEXTAFTER +#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) \ + && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) +#define CATCH_CONFIG_GLOBAL_NEXTAFTER #endif // Even if we do not think the compiler has that warning, we still have // to provide a macro that can be used by the code. #if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION +#define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION #endif #if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION #endif #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +#define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +#define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS +#define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS +#define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS #endif // The goal of this macro is to avoid evaluation of the arguments, but // still have the compiler warn on problems inside... #if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) -# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) +#define CATCH_INTERNAL_IGNORE_BUT_WARN(...) #endif #if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) -# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS #elif defined(__clang__) && (__clang_major__ < 5) -# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS #endif #if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) @@ -461,102 +470,110 @@ namespace Catch { #define CATCH_CATCH_ANON(type) catch (type) #endif -#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) +#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) \ + && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) #define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #endif // end catch_compiler_capabilities.h -#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line -#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2(name, line) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE(name, line) INTERNAL_CATCH_UNIQUE_NAME_LINE2(name, line) #ifdef CATCH_CONFIG_COUNTER -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +#define INTERNAL_CATCH_UNIQUE_NAME(name) INTERNAL_CATCH_UNIQUE_NAME_LINE(name, __COUNTER__) #else -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#define INTERNAL_CATCH_UNIQUE_NAME(name) INTERNAL_CATCH_UNIQUE_NAME_LINE(name, __LINE__) #endif +#include #include #include -#include // We need a dummy global operator<< so we can bring it into Catch namespace later -struct Catch_global_namespace_dummy {}; -std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); - -namespace Catch { +struct Catch_global_namespace_dummy { +}; +std::ostream &operator<<(std::ostream &, Catch_global_namespace_dummy); - struct CaseSensitive { enum Choice { - Yes, - No - }; }; +namespace Catch +{ - class NonCopyable { - NonCopyable( NonCopyable const& ) = delete; - NonCopyable( NonCopyable && ) = delete; - NonCopyable& operator = ( NonCopyable const& ) = delete; - NonCopyable& operator = ( NonCopyable && ) = delete; +struct CaseSensitive { + enum Choice { Yes, No }; +}; - protected: - NonCopyable(); - virtual ~NonCopyable(); - }; +class NonCopyable +{ + NonCopyable(NonCopyable const &) = delete; + NonCopyable(NonCopyable &&) = delete; + NonCopyable &operator=(NonCopyable const &) = delete; + NonCopyable &operator=(NonCopyable &&) = delete; + +protected: + NonCopyable(); + virtual ~NonCopyable(); +}; - struct SourceLineInfo { +struct SourceLineInfo { - SourceLineInfo() = delete; - SourceLineInfo( char const* _file, std::size_t _line ) noexcept - : file( _file ), - line( _line ) - {} + SourceLineInfo() = delete; + SourceLineInfo(char const *_file, std::size_t _line) noexcept : file(_file), line(_line) {} - SourceLineInfo( SourceLineInfo const& other ) = default; - SourceLineInfo& operator = ( SourceLineInfo const& ) = default; - SourceLineInfo( SourceLineInfo&& ) noexcept = default; - SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; + SourceLineInfo(SourceLineInfo const &other) = default; + SourceLineInfo &operator=(SourceLineInfo const &) = default; + SourceLineInfo(SourceLineInfo &&) noexcept = default; + SourceLineInfo &operator=(SourceLineInfo &&) noexcept = default; - bool empty() const noexcept { return file[0] == '\0'; } - bool operator == ( SourceLineInfo const& other ) const noexcept; - bool operator < ( SourceLineInfo const& other ) const noexcept; + bool empty() const noexcept + { + return file[0] == '\0'; + } + bool operator==(SourceLineInfo const &other) const noexcept; + bool operator<(SourceLineInfo const &other) const noexcept; - char const* file; - std::size_t line; - }; + char const *file; + std::size_t line; +}; - std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); +std::ostream &operator<<(std::ostream &os, SourceLineInfo const &info); - // Bring in operator<< from global namespace into Catch namespace - // This is necessary because the overload of operator<< above makes - // lookup stop at namespace Catch - using ::operator<<; +// Bring in operator<< from global namespace into Catch namespace +// This is necessary because the overload of operator<< above makes +// lookup stop at namespace Catch +using ::operator<<; - // Use this in variadic streaming macros to allow - // >> +StreamEndStop - // as well as - // >> stuff +StreamEndStop - struct StreamEndStop { - std::string operator+() const; - }; - template - T const& operator + ( T const& value, StreamEndStop ) { - return value; - } +// Use this in variadic streaming macros to allow +// >> +StreamEndStop +// as well as +// >> stuff +StreamEndStop +struct StreamEndStop { + std::string operator+() const; +}; +template +T const &operator+(T const &value, StreamEndStop) +{ + return value; } +} // namespace Catch -#define CATCH_INTERNAL_LINEINFO \ - ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) +#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo(__FILE__, static_cast(__LINE__)) // end catch_common.h -namespace Catch { +namespace Catch +{ - struct RegistrarForTagAliases { - RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); - }; +struct RegistrarForTagAliases { + RegistrarForTagAliases(char const *alias, char const *tag, SourceLineInfo const &lineInfo); +}; } // end namespace Catch -#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ +#define CATCH_REGISTER_TAG_ALIAS(alias, spec) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace \ + { \ + Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME(AutoRegisterTagAlias)(alias, spec, \ + CATCH_INTERNAL_LINEINFO); \ + } \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION // end catch_tag_alias_autoregistrar.h @@ -566,131 +583,142 @@ namespace Catch { #include -namespace Catch { +namespace Catch +{ - class TestSpec; +class TestSpec; - struct ITestInvoker { - virtual void invoke () const = 0; - virtual ~ITestInvoker(); - }; +struct ITestInvoker { + virtual void invoke() const = 0; + virtual ~ITestInvoker(); +}; - class TestCase; - struct IConfig; +class TestCase; +struct IConfig; - struct ITestCaseRegistry { - virtual ~ITestCaseRegistry(); - virtual std::vector const& getAllTests() const = 0; - virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; - }; +struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector const &getAllTests() const = 0; + virtual std::vector const &getAllTestsSorted(IConfig const &config) const = 0; +}; - bool isThrowSafe( TestCase const& testCase, IConfig const& config ); - bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); - std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); - std::vector const& getAllTestCasesSorted( IConfig const& config ); +bool isThrowSafe(TestCase const &testCase, IConfig const &config); +bool matchTest(TestCase const &testCase, TestSpec const &testSpec, IConfig const &config); +std::vector filterTests(std::vector const &testCases, TestSpec const &testSpec, + IConfig const &config); +std::vector const &getAllTestCasesSorted(IConfig const &config); -} +} // namespace Catch // end catch_interfaces_testcase.h // start catch_stringref.h +#include #include -#include #include -#include +#include -namespace Catch { +namespace Catch +{ - /// A non-owning string class (similar to the forthcoming std::string_view) - /// Note that, because a StringRef may be a substring of another string, - /// it may not be null terminated. - class StringRef { - public: - using size_type = std::size_t; - using const_iterator = const char*; +/// A non-owning string class (similar to the forthcoming std::string_view) +/// Note that, because a StringRef may be a substring of another string, +/// it may not be null terminated. +class StringRef +{ +public: + using size_type = std::size_t; + using const_iterator = const char *; - private: - static constexpr char const* const s_empty = ""; +private: + static constexpr char const *const s_empty = ""; - char const* m_start = s_empty; - size_type m_size = 0; + char const *m_start = s_empty; + size_type m_size = 0; - public: // construction - constexpr StringRef() noexcept = default; +public: // construction + constexpr StringRef() noexcept = default; - StringRef( char const* rawChars ) noexcept; + StringRef(char const *rawChars) noexcept; - constexpr StringRef( char const* rawChars, size_type size ) noexcept - : m_start( rawChars ), - m_size( size ) - {} + constexpr StringRef(char const *rawChars, size_type size) noexcept : m_start(rawChars), m_size(size) {} - StringRef( std::string const& stdString ) noexcept - : m_start( stdString.c_str() ), - m_size( stdString.size() ) - {} + StringRef(std::string const &stdString) noexcept : m_start(stdString.c_str()), m_size(stdString.size()) {} - explicit operator std::string() const { - return std::string(m_start, m_size); - } + explicit operator std::string() const + { + return std::string(m_start, m_size); + } - public: // operators - auto operator == ( StringRef const& other ) const noexcept -> bool; - auto operator != (StringRef const& other) const noexcept -> bool { - return !(*this == other); - } +public: // operators + auto operator==(StringRef const &other) const noexcept -> bool; + auto operator!=(StringRef const &other) const noexcept -> bool + { + return !(*this == other); + } - auto operator[] ( size_type index ) const noexcept -> char { - assert(index < m_size); - return m_start[index]; - } + auto operator[](size_type index) const noexcept -> char + { + assert(index < m_size); + return m_start[index]; + } - public: // named queries - constexpr auto empty() const noexcept -> bool { - return m_size == 0; - } - constexpr auto size() const noexcept -> size_type { - return m_size; - } +public: // named queries + constexpr auto empty() const noexcept -> bool + { + return m_size == 0; + } + constexpr auto size() const noexcept -> size_type + { + return m_size; + } - // Returns the current start pointer. If the StringRef is not - // null-terminated, throws std::domain_exception - auto c_str() const -> char const*; + // Returns the current start pointer. If the StringRef is not + // null-terminated, throws std::domain_exception + auto c_str() const -> char const *; - public: // substrings and searches - // Returns a substring of [start, start + length). - // If start + length > size(), then the substring is [start, size()). - // If start > size(), then the substring is empty. - auto substr( size_type start, size_type length ) const noexcept -> StringRef; +public: // substrings and searches + // Returns a substring of [start, start + length). + // If start + length > size(), then the substring is [start, size()). + // If start > size(), then the substring is empty. + auto substr(size_type start, size_type length) const noexcept -> StringRef; - // Returns the current start pointer. May not be null-terminated. - auto data() const noexcept -> char const*; + // Returns the current start pointer. May not be null-terminated. + auto data() const noexcept -> char const *; - constexpr auto isNullTerminated() const noexcept -> bool { - return m_start[m_size] == '\0'; - } + constexpr auto isNullTerminated() const noexcept -> bool + { + return m_start[m_size] == '\0'; + } - public: // iterators - constexpr const_iterator begin() const { return m_start; } - constexpr const_iterator end() const { return m_start + m_size; } - }; +public: // iterators + constexpr const_iterator begin() const + { + return m_start; + } + constexpr const_iterator end() const + { + return m_start + m_size; + } +}; - auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; - auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; +auto operator+=(std::string &lhs, StringRef const &sr) -> std::string &; +auto operator<<(std::ostream &os, StringRef const &sr) -> std::ostream &; - constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { - return StringRef( rawChars, size ); - } +constexpr auto operator"" _sr(char const *rawChars, std::size_t size) noexcept -> StringRef +{ + return StringRef(rawChars, size); +} } // namespace Catch -constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { - return Catch::StringRef( rawChars, size ); +constexpr auto operator"" _catch_sr(char const *rawChars, std::size_t size) noexcept -> Catch::StringRef +{ + return Catch::StringRef(rawChars, size); } // end catch_stringref.h // start catch_preprocessor.hpp - #define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ #define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) #define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) @@ -702,9 +730,9 @@ constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) n #define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ // MSVC needs more evaluations #define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) -#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) #else -#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) #endif #define CATCH_REC_END(...) @@ -717,26 +745,30 @@ constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) n #define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 #define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 #define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT -#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) -#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) +#define CATCH_REC_NEXT1(test, next) CATCH_DEFER(CATCH_REC_NEXT0)(test, next, 0) +#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) -#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER(CATCH_REC_NEXT(peek, CATCH_REC_LIST1))(f, peek, __VA_ARGS__) +#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER(CATCH_REC_NEXT(peek, CATCH_REC_LIST0))(f, peek, __VA_ARGS__) +#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER(CATCH_REC_NEXT(peek, CATCH_REC_LIST1))(f, peek, __VA_ARGS__) -#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) \ + , f(userdata, x) CATCH_DEFER(CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD))(f, userdata, peek, __VA_ARGS__) +#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) \ + , f(userdata, x) CATCH_DEFER(CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD))(f, userdata, peek, __VA_ARGS__) +#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) \ + f(userdata, x) CATCH_DEFER(CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD))(f, userdata, peek, __VA_ARGS__) // Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, // and passes userdata as the first parameter to each invocation, // e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) -#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) +#define CATCH_REC_LIST_UD(f, userdata, ...) \ + CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) #define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) #define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) -#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ +#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO##__VA_ARGS__ #define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ #define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF #define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) @@ -747,7 +779,8 @@ constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) n // MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF #define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) #define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ -#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) \ + (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) #endif #define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ @@ -759,585 +792,900 @@ constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) n #define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) #define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) #else -#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) -#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) \ + INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) #endif -#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ - CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) +#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...) CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST, __VA_ARGS__) #define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) -#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) -#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) -#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) -#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) -#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) -#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) -#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) -#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) -#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) -#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) +#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) \ + INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) +#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) \ + INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) +#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) \ + INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) +#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) \ + INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) +#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) \ + INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) +#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) \ + INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) +#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) \ + INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) +#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) \ + INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) +#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ + INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) +#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ + INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) #define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N -#define INTERNAL_CATCH_TYPE_GEN\ - template struct TypeList {};\ - template\ - constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ - template class...> struct TemplateTypeList{};\ - template class...Cs>\ - constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ - template\ - struct append;\ - template\ - struct rewrap;\ - template class, typename...>\ - struct create;\ - template class, typename>\ - struct convert;\ - \ - template \ - struct append { using type = T; };\ - template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ - struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ - template< template class L1, typename...E1, typename...Rest>\ - struct append, TypeList, Rest...> { using type = L1; };\ - \ - template< template class Container, template class List, typename...elems>\ - struct rewrap, List> { using type = TypeList>; };\ - template< template class Container, template class List, class...Elems, typename...Elements>\ - struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ - \ - template