Skip to content

Commit

Permalink
Add BoundaryHistory for_each method
Browse files Browse the repository at this point in the history
  • Loading branch information
wthrowe committed Sep 15, 2023
1 parent c134446 commit 595f2b3
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 0 deletions.
29 changes: 29 additions & 0 deletions src/Time/BoundaryHistory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <ostream>
#include <pup.h>
#include <pup_stl.h>
#include <type_traits>
#include <utility>

#include "DataStructures/CircularDeque.hpp"
Expand Down Expand Up @@ -150,6 +151,17 @@ class BoundaryHistory {
}
/// @}

/// Apply \p func to each entry.
///
/// The function \p func must accept two arguments, one of type
/// `const TimeStepId&` and a second of either type `const Data&`
/// or `gsl::not_null<Data*>`. (Note that `Data` may be a
/// const-qualified type.) If entries are modified, the coupling
/// cache must be cleared by calling `clear_coupling_cache()` on
/// the parent `BoundaryHistory` object.
template <typename Func>
void for_each(Func&& func) const;

protected:
~SideAccessCommon() = default;

Expand Down Expand Up @@ -349,6 +361,23 @@ class BoundaryHistory {
couplings_;
};

template <typename LocalData, typename RemoteData, typename CouplingResult>
template <bool Local, bool Mutable>
template <typename Func>
void BoundaryHistory<LocalData, RemoteData, CouplingResult>::SideAccessCommon<
Local, Mutable>::for_each(Func&& func) const {
for (auto& step : parent_data()) {
for (auto& substep : step.substeps) {
if constexpr (std::is_invocable_v<Func&, const TimeStepId&,
const Data&>) {
func(std::as_const(substep.id), std::as_const(substep.data));
} else {
func(std::as_const(substep.id), make_not_null(&substep.data));
}
}
}
}

template <typename LocalData, typename RemoteData, typename CouplingResult>
template <bool Local>
void BoundaryHistory<LocalData, RemoteData, CouplingResult>::MutableSideAccess<
Expand Down
117 changes: 117 additions & 0 deletions tests/Unit/Time/Test_BoundaryHistory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@

#include "Framework/TestingFramework.hpp"

#include <boost/functional/hash.hpp>
#include <cctype>
#include <cstddef>
#include <limits>
#include <map>
#include <string>
#include <type_traits>
#include <unordered_set>
#include <utility>
#include <vector>

Expand Down Expand Up @@ -581,11 +584,125 @@ void test_substeps() {
CHECK(CacheCheck::count() == 1);
CHECK(CacheCheck::entries.at({2.0, 1.0}) == 1);
}

template <bool Local, bool Modified>
struct ReferenceFunctor {
using ExpectedData =
tmpl::conditional_t<Local, std::string, std::vector<int>>;
std::unordered_set<TimeStepId> ids{};
std::unordered_set<ExpectedData, boost::hash<ExpectedData>> entries{};

// Templated to test that the correct types are passed.
template <typename Id, typename Data>
void operator()(Id& id, Data& data) {
static_assert(std::is_same_v<Id, const TimeStepId>);
static_assert(std::is_same_v<Data, const ExpectedData>);

CHECK(ids.insert(id).second);
CHECK(entries.insert(data).second);
if constexpr (Local) {
CHECK(static_cast<bool>(std::islower(data[0])) == Modified);
} else {
CHECK((data[0] < 0) == Modified);
}
}
};

template <bool Local, bool Modified, bool Const>
struct NotNullFunctor {
using ExpectedData =
tmpl::conditional_t<Local, std::string, std::vector<int>>;
std::unordered_set<TimeStepId> ids{};
std::unordered_set<ExpectedData, boost::hash<ExpectedData>> entries{};

// Templated to test that the correct types are passed.
template <typename Id, typename Data>
void operator()(Id& id, const gsl::not_null<Data*> data) {
static_assert(std::is_same_v<Id, const TimeStepId>);
static_assert(std::is_same_v<std::remove_const_t<Data>, ExpectedData>);
static_assert(std::is_const_v<Data> == Const);

CHECK(ids.insert(id).second);
CHECK(entries.insert(*data).second);
if constexpr (Local) {
CHECK(static_cast<bool>(std::islower((*data)[0])) == Modified);
if constexpr (not Const) {
(*data)[0] =
Modified ? std::toupper((*data)[0]) : std::tolower((*data)[0]);
}
} else {
CHECK(((*data)[0] < 0) == Modified);
if constexpr (not Const) {
(*data)[0] *= -1;
}
}
}
};

template <bool Local, bool Modified, typename Times>
void check_reference(const Times& times, const size_t expected_size) {
ReferenceFunctor<Local, Modified> func{};
times.for_each(func);
CHECK(func.ids.size() == expected_size);
}

template <bool Local, bool Modified, bool Const, typename Times>
void check_not_null(const Times& times, const size_t expected_size) {
NotNullFunctor<Local, Modified, Const> func{};
times.for_each(func);
CHECK(func.ids.size() == expected_size);
}

void test_for_each() {
INFO("for_each");

BoundaryHistoryType history{};
const BoundaryHistoryType& const_history = history;

history.local().insert(make_time_id(0.), 1, "A");
history.local().insert(make_time_id(1.), 1, "B");
history.local().insert(make_time_id(2.), 1, "C");
history.local().insert(make_time_id(2., 1), 1, "D");
const size_t local_size = 4;

history.remote().insert(make_time_id(0.), 1, std::vector{1});
history.remote().insert(make_time_id(1.), 1, std::vector{2});
history.remote().insert(make_time_id(2.), 1, std::vector{3});
history.remote().insert(make_time_id(3.), 1, std::vector{4});
history.remote().insert(make_time_id(3., 1), 1, std::vector{5});
const size_t remote_size = 5;

// The second template parameter indicates whether the data is
// expected to have been modified from its original state at that
// point. Modification happens with each call to
// `check_not_null<..., ..., false>`. Modifying twice gives the
// original data.
check_reference<true, false>(const_history.local(), local_size);
check_reference<true, false>(history.local(), local_size);
check_not_null<true, false, true>(const_history.local(), local_size);
check_not_null<true, false, false>(history.local(), local_size);
check_reference<true, true>(const_history.local(), local_size);
check_reference<true, true>(history.local(), local_size);
check_not_null<true, true, true>(const_history.local(), local_size);
check_not_null<true, true, false>(history.local(), local_size);
check_reference<true, false>(const_history.local(), local_size);

check_reference<false, false>(const_history.remote(), remote_size);
check_reference<false, false>(history.remote(), remote_size);
check_not_null<false, false, true>(const_history.remote(), remote_size);
check_not_null<false, false, false>(history.remote(), remote_size);
check_reference<false, true>(const_history.remote(), remote_size);
check_reference<false, true>(history.remote(), remote_size);
check_not_null<false, true, true>(const_history.remote(), remote_size);
check_not_null<false, true, false>(history.remote(), remote_size);
check_reference<false, false>(const_history.remote(), remote_size);
}
} // namespace

SPECTRE_TEST_CASE("Unit.Time.BoundaryHistory", "[Unit][Time]") {
test_boundary_history<true>();
test_boundary_history<false>();
test_substeps<false>();
test_substeps<true>();
test_for_each();
}

0 comments on commit 595f2b3

Please sign in to comment.