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