Line data Source code
1 0 : // Distributed under the MIT License. 2 : // See LICENSE.txt for details. 3 : 4 : #pragma once 5 : 6 : #include <cstddef> 7 : #include <cstdint> 8 : #include <map> 9 : #include <optional> 10 : #include <unordered_set> 11 : 12 : #include "DataStructures/DataBox/DataBox.hpp" 13 : #include "Parallel/AlgorithmExecution.hpp" 14 : #include "Time/ChangeSlabSize/ChangeSlabSize.hpp" 15 : #include "Time/ChangeSlabSize/Tags.hpp" 16 : #include "Time/Tags/HistoryEvolvedVariables.hpp" 17 : #include "Time/TimeSteppers/TimeStepper.hpp" 18 : #include "Utilities/Algorithm.hpp" 19 : #include "Utilities/ErrorHandling/Assert.hpp" 20 : #include "Utilities/TMPL.hpp" 21 : 22 : /// \cond 23 : namespace Parallel { 24 : template <typename Metavariables> 25 : class GlobalCache; 26 : } // namespace Parallel 27 : namespace Tags { 28 : struct TimeStepId; 29 : template <typename StepperInterface> 30 : struct TimeStepper; 31 : } // namespace Tags 32 : namespace tuples { 33 : template <class... Tags> 34 : class TaggedTuple; 35 : } // namespace tuples 36 : /// \endcond 37 : 38 : namespace Actions { 39 : /// \ingroup ActionsGroup 40 : /// \ingroup TimeGroup 41 : /// Adjust the slab size based on previous executions of 42 : /// Events::ChangeSlabSize 43 : /// 44 : /// Uses: 45 : /// - DataBox: 46 : /// - Tags::HistoryEvolvedVariables 47 : /// - Tags::TimeStep 48 : /// - Tags::TimeStepId 49 : /// - Tags::TimeStepper<TimeStepper> 50 : /// 51 : /// DataBox changes: 52 : /// - Adds: nothing 53 : /// - Removes: nothing 54 : /// - Modifies: 55 : /// - Tags::Next<Tags::TimeStepId> 56 : /// - Tags::TimeStep 57 : /// - Tags::TimeStepId 58 1 : struct ChangeSlabSize { 59 0 : using simple_tags = 60 : tmpl::list<::Tags::ChangeSlabSize::NewSlabSize, 61 : ::Tags::ChangeSlabSize::NumberOfExpectedMessages>; 62 : 63 : template <typename DbTags, typename... InboxTags, typename Metavariables, 64 : typename ArrayIndex, typename ActionList, 65 : typename ParallelComponent> 66 0 : static Parallel::iterable_action_return_t apply( 67 : db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/, 68 : const Parallel::GlobalCache<Metavariables>& /*cache*/, 69 : const ArrayIndex& /*array_index*/, const ActionList /*meta*/, 70 : const ParallelComponent* const /*meta*/) { 71 : const auto& time_step_id = db::get<::Tags::TimeStepId>(box); 72 : if (not time_step_id.is_at_slab_boundary()) { 73 : return {Parallel::AlgorithmExecution::Continue, std::nullopt}; 74 : } 75 : 76 : const auto slab_number = time_step_id.slab_number(); 77 : const auto& expected_messages_map = 78 : db::get<::Tags::ChangeSlabSize::NumberOfExpectedMessages>(box); 79 : if (expected_messages_map.empty() or 80 : expected_messages_map.begin()->first != slab_number) { 81 : return {Parallel::AlgorithmExecution::Continue, std::nullopt}; 82 : } 83 : const size_t expected_messages = expected_messages_map.begin()->second; 84 : ASSERT(expected_messages > 0, 85 : "Should only create map entries when sending messages."); 86 : 87 : const auto& slab_size_messages = 88 : db::get<::Tags::ChangeSlabSize::NewSlabSize>(box); 89 : if (slab_size_messages.empty()) { 90 : return {Parallel::AlgorithmExecution::Retry, std::nullopt}; 91 : } 92 : const int64_t first_received_change_slab = 93 : slab_size_messages.begin()->first; 94 : 95 : ASSERT(first_received_change_slab >= slab_number, 96 : "Received data for a change at slab " << first_received_change_slab 97 : << " but it is already slab " << slab_number); 98 : if (first_received_change_slab != slab_number) { 99 : return {Parallel::AlgorithmExecution::Retry, std::nullopt}; 100 : } 101 : 102 : const auto& received_changes = slab_size_messages.begin()->second; 103 : ASSERT(expected_messages >= received_changes.size(), 104 : "Received " << received_changes.size() 105 : << " size change messages at slab " 106 : << slab_number << ", but only expected " 107 : << expected_messages); 108 : if (received_changes.size() != expected_messages) { 109 : return {Parallel::AlgorithmExecution::Retry, std::nullopt}; 110 : } 111 : 112 : // We have all the data we need. 113 : 114 : const double new_slab_size = 115 : *alg::min_element(slab_size_messages.begin()->second); 116 : 117 : db::mutate<::Tags::ChangeSlabSize::NumberOfExpectedMessages, 118 : ::Tags::ChangeSlabSize::NewSlabSize>( 119 : [](const gsl::not_null<std::map<int64_t, size_t>*> expected, 120 : const gsl::not_null< 121 : std::map<int64_t, std::unordered_multiset<double>>*> 122 : sizes) { 123 : expected->erase(expected->begin()); 124 : sizes->erase(sizes->begin()); 125 : }, 126 : make_not_null(&box)); 127 : 128 : const TimeStepper& time_stepper = 129 : db::get<::Tags::TimeStepper<TimeStepper>>(box); 130 : 131 : // Sometimes time steppers need to run with a fixed step size. 132 : // This is generally at the start of an evolution when the history 133 : // is in an unusual state. 134 : if (not time_stepper.can_change_step_size( 135 : time_step_id, db::get<::Tags::HistoryEvolvedVariables<>>(box))) { 136 : return {Parallel::AlgorithmExecution::Continue, std::nullopt}; 137 : } 138 : 139 : const auto current_slab = time_step_id.step_time().slab(); 140 : 141 : const double new_slab_end = 142 : time_step_id.time_runs_forward() 143 : ? current_slab.start().value() + new_slab_size 144 : : current_slab.end().value() - new_slab_size; 145 : 146 : change_slab_size(make_not_null(&box), new_slab_end); 147 : 148 : return {Parallel::AlgorithmExecution::Continue, std::nullopt}; 149 : } 150 : }; 151 : } // namespace Actions