diff --git a/include/heyoka/model/sgp4.hpp b/include/heyoka/model/sgp4.hpp index cfbf0b0b1..955476074 100644 --- a/include/heyoka/model/sgp4.hpp +++ b/include/heyoka/model/sgp4.hpp @@ -69,6 +69,10 @@ class HEYOKA_DLL_PUBLIC_INLINE_CLASS sgp4_propagator template static auto parse_ctor_args(const Input &in, const KwArgs &...kw_args) { + if (in.data_handle() == nullptr) [[unlikely]] { + throw std::invalid_argument("Cannot initialise an sgp4_propagator with a null list of satellites"); + } + if (in.extent(1) == 0u) [[unlikely]] { throw std::invalid_argument("Cannot initialise an sgp4_propagator with an empty list of satellites"); } diff --git a/test/model_sgp4.cpp b/test/model_sgp4.cpp index 442c0f69b..316205a6e 100644 --- a/test/model_sgp4.cpp +++ b/test/model_sgp4.cpp @@ -7,6 +7,8 @@ // with this file, You can obtain one at http://mozilla.org/MPL/2.0/. #include +#include +#include #include #include @@ -150,7 +152,7 @@ TEST_CASE("propagator") const prop_t::in_1d tm_in{tm.data(), 2}; for (auto cm : {false, true}) { - prop_t prop{md_input_t{ins.data(), 9, 2}, kw::compact_mode = cm}; + prop_t prop{md_input_t{ins.data(), 2}, kw::compact_mode = cm}; std::vector outs(12u); prop_t::out_2d out{outs.data(), 6, 2}; @@ -222,7 +224,7 @@ TEST_CASE("propagator batch") const prop_t::in_2d tm_in{tm.data(), 2, 2}; for (auto cm : {false, true}) { - prop_t prop{md_input_t{ins.data(), 9, 2}, kw::compact_mode = cm}; + prop_t prop{md_input_t{ins.data(), 2}, kw::compact_mode = cm}; std::vector outs(24u); prop_t::out_3d out{outs.data(), 2, 6, 2}; @@ -288,3 +290,84 @@ TEST_CASE("propagator batch") REQUIRE(out(1, 5, 1) == approximately(1.785277174529374, 10000.)); } } + +TEST_CASE("error handling") +{ + detail::edb_disabler ed; + + using Catch::Matchers::Message; + using md_input_t = mdspan>; + using prop_t = model::sgp4_propagator; + + // Propagator with null list or zero satellites. + REQUIRE_THROWS_MATCHES((prop_t{md_input_t{nullptr, 0}}), std::invalid_argument, + Message("Cannot initialise an sgp4_propagator with a null list of satellites")); + + std::vector input(9u); + + REQUIRE_THROWS_MATCHES((prop_t{md_input_t{input.data(), 0}}), std::invalid_argument, + Message("Cannot initialise an sgp4_propagator with an empty list of satellites")); +} + +TEST_CASE("derivatives") +{ + detail::edb_disabler ed; + + using md_input_t = mdspan>; + using prop_t = model::sgp4_propagator; + + // First compute the order-2 derivatives of the whole model. + const auto sgp4_func = model::sgp4(); + const auto inputs = make_vars("n0", "e0", "i0", "node0", "omega0", "m0", "bstar", "tsince"); + const auto dt = diff_tensors(sgp4_func, std::vector(inputs.begin(), inputs.begin() + 6), kw::diff_order = 2); + + // Make a compiled function with the derivatives. + auto diff_cf = cfunc(dt | std::views::transform([](const auto &p) { return p.second; }), inputs); + + // Create a propagator with derivatives. + const std::vector ins = {revday2radmin(13.75091047972192), + revday2radmin(15.50103472202482), + 0.0024963, + 0.0007417, + deg2rad(90.2039), + deg2rad(51.6439), + deg2rad(55.5633), + deg2rad(211.2001), + deg2rad(320.5956), + deg2rad(17.6667), + deg2rad(91.4738), + deg2rad(85.6398), + 0.75863e-3, + .38792e-4, + 2460486.5, + 2458826.5, + 0.6478633000000116, + 0.6933954099999937}; + + const auto tm = std::array{1440., 0.}; + const prop_t::in_1d tm_in{tm.data(), 2}; + + prop_t prop{md_input_t{ins.data(), 2}, kw::diff_order = 2}; + + // Prepare the input buffer for the cfunc. + std::vector cf_in(ins.begin(), ins.begin() + 14); + cf_in.insert(cf_in.end(), tm.begin(), tm.end()); + cfunc::in_2d cf_in_span(cf_in.data(), 8, 2); + + // Prepare the output buffers. + std::vector cf_out(dt.size() * 2u), prop_out(cf_out); + cfunc::out_2d cf_out_span(cf_out.data(), dt.size(), 2); + cfunc::out_2d prop_out_span(prop_out.data(), dt.size(), 2); + + // Evaluate the cfunc. + diff_cf(cf_out_span, cf_in_span); + + // Evaluate the propagation. + prop(prop_out_span, tm_in); + + for (std::size_t i = 0; i < prop_out_span.extent(0); ++i) { + for (std::size_t j = 0; j < prop_out_span.extent(1); ++j) { + REQUIRE(prop_out_span(i, j) == approximately(cf_out_span(i, j))); + } + } +}