From dae7cd0fd7562acc884e094de2799593708ca980 Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Thu, 18 Jul 2024 08:38:01 +0200 Subject: [PATCH 1/2] Add function to replace the satellite data. --- include/heyoka/model/sgp4.hpp | 2 + src/model/sgp4.cpp | 73 +++++++++++++++++++- test/model_sgp4.cpp | 121 ++++++++++++++++++++++++++++++++++ 3 files changed, 193 insertions(+), 3 deletions(-) diff --git a/include/heyoka/model/sgp4.hpp b/include/heyoka/model/sgp4.hpp index da3510351..f4e828fbc 100644 --- a/include/heyoka/model/sgp4.hpp +++ b/include/heyoka/model/sgp4.hpp @@ -169,6 +169,8 @@ class HEYOKA_DLL_PUBLIC_INLINE_CLASS sgp4_propagator [[nodiscard]] std::uint32_t get_nouts() const noexcept; [[nodiscard]] mdspan> get_sat_data() const; + void replace_sat_data(mdspan>); + [[nodiscard]] std::uint32_t get_diff_order() const noexcept; [[nodiscard]] const std::vector &get_diff_args() const; [[nodiscard]] std::pair get_dslice(std::uint32_t) const; diff --git a/src/model/sgp4.cpp b/src/model/sgp4.cpp index 23908d5cc..f7d788813 100644 --- a/src/model/sgp4.cpp +++ b/src/model/sgp4.cpp @@ -510,6 +510,7 @@ struct sgp4_propagator::impl { std::vector m_sat_buffer; std::vector m_init_buffer; cfunc m_cf_tprop; + cfunc m_cf_init; std::optional m_dtens; // NOTE: this is a buffer used to convert dates to tsinces in the call operator // overloads taking dates in input. @@ -522,6 +523,7 @@ struct sgp4_propagator::impl { ar & m_sat_buffer; ar & m_init_buffer; ar & m_cf_tprop; + ar & m_cf_init; ar & m_dtens; // NOTE: no need to save the content of m_tms_vec. } @@ -625,7 +627,7 @@ sgp4_propagator::sgp4_propagator(ptag, std::tuple, cfunc, c std::vector init_buffer; init_buffer.resize(boost::safe_numerics::safe(n_sats) * cf_init.get_nouts()); - // Prepare the in/out spans for invocation of cf_init. + // Prepare the in/out spans for the invocation of cf_init. // NOTE: for initialisation we only need to read the elements and the bstars from sat_buffer, // the epochs do not matter. Hence, 7 rows instead of 9. const typename cfunc::in_2d init_input(sat_buffer.data(), 7, boost::numeric_cast(n_sats)); @@ -637,8 +639,8 @@ sgp4_propagator::sgp4_propagator(ptag, std::tuple, cfunc, c cf_init(init_output, init_input); // Build and assign the implementation. - m_impl = std::make_unique( - impl{std::move(sat_buffer), std::move(init_buffer), std::move(cf_tprop), std::move(dt), {}}); + m_impl = std::make_unique(impl{ + std::move(sat_buffer), std::move(init_buffer), std::move(cf_tprop), std::move(cf_init), std::move(dt), {}}); } template @@ -692,6 +694,71 @@ mdspan> sgp4_propagator m_impl->m_sat_buffer.data(), boost::numeric_cast(m_impl->m_sat_buffer.size() / 9u)}; } +template + requires std::same_as || std::same_as +void sgp4_propagator::replace_sat_data(mdspan> new_data) +{ + // Cache nsats. + const auto nsats = get_nsats(); + + if (new_data.data_handle() == nullptr) [[unlikely]] { + throw std::invalid_argument("Cannot replace the satellite data with a null array"); + } + + if (new_data.extent(1) != nsats) [[unlikely]] { + throw std::invalid_argument(fmt::format("Invalid array provided to replace_sat_data(): the number of " + "columns ({}) does not match the number of satellites ({})", + new_data.extent(1), nsats)); + } + + // Fetch references to sat_buffer and init_buffer. + auto &sat_buffer = m_impl->m_sat_buffer; + auto &init_buffer = m_impl->m_init_buffer; + + // Make copies of the existing data for exception safety. + // NOTE: the concern here is mostly about sat_buffer, since the user may be + // providing invalid data. However, in principle, cf_init could also throw + // when invoked, thus we save also init_buffer. + const auto old_sat_buffer = sat_buffer; + const auto old_init_buffer = init_buffer; + + try { + // Write the new data into sat_buffer. + const mdspan> buffer_span(sat_buffer.data(), + new_data.extent(1)); + for (std::size_t i = 0; i < buffer_span.extent(0); ++i) { + for (std::size_t j = 0; j < buffer_span.extent(1); ++j) { + buffer_span(i, j) = new_data(i, j); + } + } + + // Check the new data. + detail::sgp4_check_input_satbuf(sat_buffer); + + // Fetch a reference to cf_init. + auto &cf_init = m_impl->m_cf_init; + + // Prepare the in/out spans for the invocation of cf_init. + // NOTE: for initialisation we only need to read the elements and the bstars from sat_buffer, + // the epochs do not matter. Hence, 7 rows instead of 9. + // NOTE: static casts are ok, we already inited once during construction. + const typename cfunc::in_2d init_input(sat_buffer.data(), 7, static_cast(nsats)); + const typename cfunc::out_2d init_output(init_buffer.data(), static_cast(cf_init.get_nouts()), + static_cast(nsats)); + + // Evaluate the intermediate quantities and their derivatives. + cf_init(init_output, init_input); + } catch (...) { + // Restore the old data before rethrowing. + // NOTE: copy, don't move, as we need to make sure to never + // destroy/reallocate the existing buffers. + std::ranges::copy(old_sat_buffer, sat_buffer.begin()); + std::ranges::copy(old_init_buffer, init_buffer.begin()); + + throw; + } +} + template requires std::same_as || std::same_as void sgp4_propagator::check_with_diff(const char *fname) const diff --git a/test/model_sgp4.cpp b/test/model_sgp4.cpp index 3b2fad58f..d6f412b2c 100644 --- a/test/model_sgp4.cpp +++ b/test/model_sgp4.cpp @@ -726,3 +726,124 @@ TEST_CASE("s11n") REQUIRE(prop.get_nsats() == 2u); REQUIRE(sat_data == new_sat_data); } + +TEST_CASE("replace_sat_data") +{ + using Catch::Matchers::Message; + + detail::edb_disabler ed; + + using prop_t = model::sgp4_propagator; + + using md_input_t = mdspan>; + + 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}; + + prop_t prop{md_input_t{ins.data(), 2}, kw::diff_order = 1}; + + // Build a second propagator with the two satellites' data swapped. + std::vector ins2 = {revday2radmin(15.50103472202482), + revday2radmin(13.75091047972192), + 0.0007417, + 0.0024963, + deg2rad(51.6439), + deg2rad(90.2039), + deg2rad(211.2001), + deg2rad(55.5633), + deg2rad(17.6667), + deg2rad(320.5956), + deg2rad(85.6398), + deg2rad(91.4738), + .38792e-4, + 0.75863e-3, + 2458826.5, + 2460486.5, + 0.6933954099999937, + 0.6478633000000116}; + + prop_t prop2{md_input_t{ins2.data(), 2}, kw::diff_order = 1}; + + // Replace the data in prop with the data in prop2. + prop.replace_sat_data(md_input_t{ins2.data(), 2}); + + // Check that the replacement worked. + REQUIRE(std::vector(prop.get_sat_data().data_handle(), prop.get_sat_data().data_handle() + 18) + == std::vector(prop2.get_sat_data().data_handle(), prop2.get_sat_data().data_handle() + 18)); + + // Run evaluations in prop and prop2 and compare the results. + std::vector times; + times.resize(2u, prop_t::date{2460486.5, 0.6478633000000116}); + + std::vector outs; + outs.resize(2 * prop.get_nouts()); + prop_t::out_2d out_span{outs.data(), prop.get_nouts(), 2}; + + prop(out_span, prop_t::in_1d{times.data(), 2}); + + const auto orig_out = outs; + + prop2(out_span, prop_t::in_1d{times.data(), 2}); + + REQUIRE(orig_out == outs); + + // Also check with s11n. + { + std::stringstream ss; + + { + boost::archive::binary_oarchive oa(ss); + oa << prop; + } + + prop = prop_t{}; + + { + boost::archive::binary_iarchive ia(ss); + ia >> prop; + } + + prop(out_span, prop_t::in_1d{times.data(), 2}); + + REQUIRE(orig_out == outs); + } + + // Check with bad data. + ins2[1] = revday2radmin(3); + + REQUIRE_THROWS_MATCHES(prop.replace_sat_data(md_input_t{ins2.data(), 2}), std::invalid_argument, + Message("The satellite at index 1 has an orbital period above 225 minutes, but deep-" + "space propagation is currently not supported")); + + // Check that the original data was restored after the exception was thrown. + REQUIRE(std::vector(prop.get_sat_data().data_handle(), prop.get_sat_data().data_handle() + 18) + == std::vector(prop2.get_sat_data().data_handle(), prop2.get_sat_data().data_handle() + 18)); + + prop(out_span, prop_t::in_1d{times.data(), 2}); + + REQUIRE(orig_out == outs); + + // Error throwing. + REQUIRE_THROWS_MATCHES((prop.replace_sat_data(md_input_t{nullptr, 2})), std::invalid_argument, + Message("Cannot replace the satellite data with a null array")); + + REQUIRE_THROWS_MATCHES((prop.replace_sat_data(md_input_t{ins2.data(), 1})), std::invalid_argument, + Message("Invalid array provided to replace_sat_data(): the number of " + "columns (1) does not match the number of satellites (2)")); +} From 86c25bbd2f86c0543b0e5e4f79944bec721e4673 Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Thu, 18 Jul 2024 10:59:41 +0200 Subject: [PATCH 2/2] Simplify several checks around mdspans. It is really not up to us to check for nonempty nullptr mdspans, as the creation of a nonempty nullptr mdspan is itself UB. Thus, the only checks we need are on the shape of the mdspan. --- include/heyoka/model/sgp4.hpp | 3 --- src/cfunc_class.cpp | 34 ---------------------------------- src/model/sgp4.cpp | 28 ---------------------------- test/cfunc.cpp | 22 ---------------------- test/cfunc_multieval.cpp | 24 ------------------------ test/model_sgp4.cpp | 20 +------------------- 6 files changed, 1 insertion(+), 130 deletions(-) diff --git a/include/heyoka/model/sgp4.hpp b/include/heyoka/model/sgp4.hpp index f4e828fbc..ee9b29a78 100644 --- a/include/heyoka/model/sgp4.hpp +++ b/include/heyoka/model/sgp4.hpp @@ -87,9 +87,6 @@ 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/src/cfunc_class.cpp b/src/cfunc_class.cpp index 225dba95e..56629dce7 100644 --- a/src/cfunc_class.cpp +++ b/src/cfunc_class.cpp @@ -452,21 +452,12 @@ void cfunc::single_eval(out_1d outputs, in_1d inputs, std::optional pa m_impl->m_nouts, outputs.size())); } - if (outputs.data_handle() == nullptr) [[unlikely]] { - throw std::invalid_argument("The outputs array passed to a cfunc cannot be null"); - } - if (inputs.size() != m_impl->m_nvars) [[unlikely]] { throw std::invalid_argument(fmt::format("Invalid inputs array passed to a cfunc: the number of function " "inputs is {}, but the inputs array has a size of {}", m_impl->m_nvars, inputs.size())); } - if (inputs.data_handle() == nullptr && !inputs.empty()) [[unlikely]] { - throw std::invalid_argument( - "The inputs array passed to a cfunc can be null only if the number of input arguments is zero"); - } - if (m_impl->m_nparams != 0u && !pars) [[unlikely]] { throw std::invalid_argument( "An array of parameter values must be passed in order to evaluate a function with parameters"); @@ -479,11 +470,6 @@ void cfunc::single_eval(out_1d outputs, in_1d inputs, std::optional pa "but the number of parameters in the function is {}", pars->size(), m_impl->m_nparams)); } - - if (pars->data_handle() == nullptr && !pars->empty()) [[unlikely]] { - throw std::invalid_argument( - "The array of parameter values passed to a cfunc can be null only if the number of parameters is zero"); - } } if (m_impl->m_is_time_dependent && !time) [[unlikely]] { @@ -719,11 +705,6 @@ void cfunc::multi_eval(out_2d outputs, in_2d inputs, std::optional par m_impl->m_nouts, outputs.extent(0))); } - if (outputs.data_handle() == nullptr && !outputs.empty()) [[unlikely]] { - throw std::invalid_argument( - "The outputs array passed to a cfunc can be null only if the number of evaluations is zero"); - } - // Fetch the number of columns from outputs. const auto ncols = outputs.extent(1); @@ -740,11 +721,6 @@ void cfunc::multi_eval(out_2d outputs, in_2d inputs, std::optional par ncols, inputs.extent(1))); } - if (inputs.data_handle() == nullptr && !inputs.empty()) [[unlikely]] { - throw std::invalid_argument("The inputs array passed to a cfunc can be null only if the number of input " - "arguments or the number of evaluations is zero"); - } - if (m_impl->m_nparams != 0u && !pars) [[unlikely]] { throw std::invalid_argument( "An array of parameter values must be passed in order to evaluate a function with parameters"); @@ -765,11 +741,6 @@ void cfunc::multi_eval(out_2d outputs, in_2d inputs, std::optional par "outputs array is {}", pars->extent(1), ncols)); } - - if (pars->data_handle() == nullptr && !pars->empty()) [[unlikely]] { - throw std::invalid_argument("The array of parameter values passed to a cfunc can be null only if the " - "number of parameters or the number of evaluations is zero"); - } } if (m_impl->m_is_time_dependent && !times) [[unlikely]] { @@ -785,11 +756,6 @@ void cfunc::multi_eval(out_2d outputs, in_2d inputs, std::optional par "outputs array is {}", times->size(), ncols)); } - - if (times->data_handle() == nullptr && !times->empty()) [[unlikely]] { - throw std::invalid_argument("The array of time values passed to a cfunc can be null only if the " - "number of evaluations is zero"); - } } #if defined(HEYOKA_HAVE_REAL) diff --git a/src/model/sgp4.cpp b/src/model/sgp4.cpp index f7d788813..12bd647b3 100644 --- a/src/model/sgp4.cpp +++ b/src/model/sgp4.cpp @@ -701,10 +701,6 @@ void sgp4_propagator::replace_sat_data(mdspan void sgp4_propagator::operator()(out_2d out, in_1d dates) { // Check the dates array. - if (dates.data_handle() == nullptr) [[unlikely]] { - throw std::invalid_argument("A null array of dates was passed to the call operator of an sgp4_propagator"); - } const auto n_sats = get_nsats(); if (dates.extent(0) != n_sats) [[unlikely]] { throw std::invalid_argument( @@ -948,23 +941,6 @@ template requires std::same_as || std::same_as void sgp4_propagator::operator()(out_3d out, in_2d tms) { - // NOTE: need to check for nullptr input spans. In the non-batch overload - // we do not need the explicit check because we don't do anything with 'out' - // and 'tms' apart from forwarding them to the cfunc, which does the nullptr check. - // Here however we need to take subspans of 'out' and 'tms' and thus we need to - // pre-check for nullptr in order to avoid undefined behaviour - see the docs for - // the def ctor of mdspan: - // - // https://en.cppreference.com/w/cpp/container/mdspan/mdspan - if (out.data_handle() == nullptr) [[unlikely]] { - throw std::invalid_argument( - "A null output array was passed to the batch-mode call operator of an sgp4_propagator"); - } - if (tms.data_handle() == nullptr) [[unlikely]] { - throw std::invalid_argument( - "A null times array was passed to the batch-mode call operator of an sgp4_propagator"); - } - // Check the dimensionalities of out and tms. const auto n_evals = out.extent(0); if (n_evals != tms.extent(0)) [[unlikely]] { @@ -1039,10 +1015,6 @@ template void sgp4_propagator::operator()(out_3d out, in_2d dates) { // Check the dates array. - if (dates.data_handle() == nullptr) [[unlikely]] { - throw std::invalid_argument( - "A null array of dates was passed to the batch-mode call operator of an sgp4_propagator"); - } const auto n_sats = get_nsats(); if (dates.extent(1) != n_sats) [[unlikely]] { throw std::invalid_argument(fmt::format( diff --git a/test/cfunc.cpp b/test/cfunc.cpp index 036c58f35..b887fce76 100644 --- a/test/cfunc.cpp +++ b/test/cfunc.cpp @@ -350,19 +350,6 @@ TEST_CASE("single call operator") REQUIRE(output2[1] == -8); std::ranges::fill(output2, fp_t(0)); - // Null output span. - REQUIRE_THROWS_MATCHES( - cf0(typename cfunc::out_1d{nullptr, 2u}, std::array{}, kw::pars = par1, kw::time = fp_t(10)), - std::invalid_argument, Message("The outputs array passed to a cfunc cannot be null")); - - // Null input span with inputs. - cf0 = cfunc({x + y - heyoka::time, x - y + par[0]}, {x, y}, kw::opt_level = opt_level, - kw::high_accuracy = high_accuracy, kw::compact_mode = compact_mode); - REQUIRE_THROWS_MATCHES( - cf0(output2, typename cfunc::in_1d{nullptr, 2}, kw::pars = par1, kw::time = fp_t(10)), - std::invalid_argument, - Message("The inputs array passed to a cfunc can be null only if the number of input arguments is zero")); - // Null par span with no pars. cf0 = cfunc({x + y - heyoka::time, x - y}, {x, y}, kw::opt_level = opt_level, kw::high_accuracy = high_accuracy, kw::compact_mode = compact_mode); @@ -373,15 +360,6 @@ TEST_CASE("single call operator") REQUIRE(output2[0] == -7); REQUIRE(output2[1] == -1); std::ranges::fill(output2, fp_t(0)); - - // Null par span with pars. - cf0 = cfunc({x + y - heyoka::time, x - y + par[0]}, {x, y}, kw::opt_level = opt_level, - kw::high_accuracy = high_accuracy, kw::compact_mode = compact_mode); - REQUIRE_THROWS_MATCHES(cf0(output2, std::array{1, 2}, - kw::pars = typename cfunc::in_1d{nullptr, 1}, kw::time = fp_t(10)), - std::invalid_argument, - Message("The array of parameter values passed to a cfunc can be null only if the number " - "of parameters is zero")); }; for (auto cm : {false, true}) { diff --git a/test/cfunc_multieval.cpp b/test/cfunc_multieval.cpp index 9b9ce8bd4..ab3756150 100644 --- a/test/cfunc_multieval.cpp +++ b/test/cfunc_multieval.cpp @@ -92,11 +92,6 @@ TEST_CASE("multieval st") // Check no error on zero nevals with null outputs span. REQUIRE_NOTHROW(cf0(out_2d{nullptr, 2, 0}, in_2d{ibuf.data(), 2, 0})); - // Check error with null outputs span and nonzero evals. - REQUIRE_THROWS_MATCHES( - cf0(out_2d{nullptr, 2, 10}, in_2d{ibuf.data(), 0, 0}), std::invalid_argument, - Message("The outputs array passed to a cfunc can be null only if the number of evaluations is zero")); - obuf.resize(20u); REQUIRE_THROWS_MATCHES(cf0(out_2d{obuf.data(), 2, 10}, in_2d{ibuf.data(), 0, 0}), std::invalid_argument, @@ -113,11 +108,6 @@ TEST_CASE("multieval st") Message("Invalid inputs array passed to a cfunc: the expected number of columns deduced from the " "outputs array is 10, but the number of columns in the inputs array is 5")); - // Null input span. - REQUIRE_THROWS_MATCHES(cf0(out_2d{obuf.data(), 2, 10}, in_2d{nullptr, 2, 10}), std::invalid_argument, - Message("The inputs array passed to a cfunc can be null only if the number of input " - "arguments or the number of evaluations is zero")); - cf0 = cfunc{{x + y + par[0], x - y + heyoka::time}, {x, y}, kw::opt_level = opt_level, @@ -167,20 +157,6 @@ TEST_CASE("multieval st") "but the expected size deduced from the " "outputs array is 10")); - // Null par span. - REQUIRE_THROWS_MATCHES(cf0(out_2d{obuf.data(), 2, 10}, in_2d{ibuf.data(), 2, 10}, - kw::pars = in_2d{nullptr, 1, 10}, kw::time = in_1d{tbuf.data(), 5}), - std::invalid_argument, - Message("The array of parameter values passed to a cfunc can be null only if the " - "number of parameters or the number of evaluations is zero")); - - // Null time span. - REQUIRE_THROWS_MATCHES(cf0(out_2d{obuf.data(), 2, 10}, in_2d{ibuf.data(), 2, 10}, - kw::pars = in_2d{pbuf.data(), 1, 10}, kw::time = in_1d{nullptr, 10}), - std::invalid_argument, - Message("The array of time values passed to a cfunc can be null only if the " - "number of evaluations is zero")); - // Functional testing. cf0 = cfunc{{x + y, x - y}, {x, y}, diff --git a/test/model_sgp4.cpp b/test/model_sgp4.cpp index d6f412b2c..f06ddd097 100644 --- a/test/model_sgp4.cpp +++ b/test/model_sgp4.cpp @@ -263,9 +263,7 @@ TEST_CASE("propagator single") REQUIRE(out(6, 1) == 0.); // Try with several bogus input spans. - REQUIRE_THROWS_AS(prop(prop_t::out_2d{nullptr, 7, 2}, date_in), std::invalid_argument); REQUIRE_THROWS_AS(prop(prop_t::out_2d{outs.data(), 5, 2}, date_in), std::invalid_argument); - REQUIRE_THROWS_AS(prop(out, prop_t::in_1d{nullptr, 2}), std::invalid_argument); REQUIRE_THROWS_AS(prop(out, prop_t::in_1d{ins.data(), 1}), std::invalid_argument); } } @@ -380,14 +378,8 @@ TEST_CASE("propagator batch") prop(prop_t::out_3d{outs.data(), 0, 7, 2}, prop_t::in_2d{tm.data(), 0, 2}); // Try with several bogus input spans. - REQUIRE_THROWS_MATCHES( - prop(prop_t::out_3d{nullptr, 2, 7, 2}, date_in), std::invalid_argument, - Message("A null output array was passed to the batch-mode call operator of an sgp4_propagator")); REQUIRE_THROWS_AS(prop(prop_t::out_3d{outs.data(), 2, 5, 2}, date_in), std::invalid_argument); REQUIRE_THROWS_AS(prop(prop_t::out_3d{outs.data(), 2, 4, 1}, date_in), std::invalid_argument); - REQUIRE_THROWS_MATCHES( - prop(out, prop_t::in_2d{nullptr, 2, 2}), std::invalid_argument, - Message("A null times array was passed to the batch-mode call operator of an sgp4_propagator")); REQUIRE_THROWS_AS(prop(out, prop_t::in_2d{ins.data(), 2, 1}), std::invalid_argument); REQUIRE_THROWS_AS(prop(out, prop_t::in_2d{ins.data(), 2, 0}), std::invalid_argument); } @@ -403,7 +395,7 @@ TEST_CASE("error handling") // 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")); + Message("Cannot initialise an sgp4_propagator with an empty list of satellites")); std::vector input(9u); @@ -467,9 +459,6 @@ TEST_CASE("error handling") Message("Invalid propagation date detected for the satellite at index 1: the magnitude of the Julian " "date (0) is less than the magnitude of the fractional correction (1)")); - REQUIRE_THROWS_MATCHES(prop(out, prop_t::in_1d{nullptr, 2}), std::invalid_argument, - Message("A null array of dates was passed to the call operator of an sgp4_propagator")); - prop_t::in_1d date_in2{dates.data(), 1}; REQUIRE_THROWS_MATCHES( @@ -492,10 +481,6 @@ TEST_CASE("error handling") "inferred from the output array is 2, which is not consistent with the number of evaluations " "inferred from the times array (1)")); - REQUIRE_THROWS_MATCHES( - prop(out_batch, prop_t::in_2d{nullptr, 1, 2}), std::invalid_argument, - Message("A null array of dates was passed to the batch-mode call operator of an sgp4_propagator")); - date_b = prop_t::in_2d{dates_batch.data(), 1, 1}; REQUIRE_THROWS_MATCHES( @@ -840,9 +825,6 @@ TEST_CASE("replace_sat_data") REQUIRE(orig_out == outs); // Error throwing. - REQUIRE_THROWS_MATCHES((prop.replace_sat_data(md_input_t{nullptr, 2})), std::invalid_argument, - Message("Cannot replace the satellite data with a null array")); - REQUIRE_THROWS_MATCHES((prop.replace_sat_data(md_input_t{ins2.data(), 1})), std::invalid_argument, Message("Invalid array provided to replace_sat_data(): the number of " "columns (1) does not match the number of satellites (2)"));