Line data Source code
1 0 : // Distributed under the MIT License. 2 : // See LICENSE.txt for details. 3 : 4 : #pragma once 5 : 6 : #include <limits> 7 : #include <optional> 8 : #include <tuple> 9 : 10 : #include "DataStructures/DataBox/DataBox.hpp" 11 : #include "DataStructures/DataBox/Prefixes.hpp" // IWYU pragma: keep // for Tags::Next 12 : #include "Parallel/AlgorithmExecution.hpp" 13 : #include "Time/Actions/UpdateU.hpp" 14 : #include "Time/AdaptiveSteppingDiagnostics.hpp" 15 : #include "Time/ChooseLtsStepSize.hpp" 16 : #include "Time/Tags/AdaptiveSteppingDiagnostics.hpp" 17 : #include "Time/Tags/HistoryEvolvedVariables.hpp" 18 : #include "Time/TimeSteppers/LtsTimeStepper.hpp" 19 : #include "Utilities/Gsl.hpp" 20 : #include "Utilities/TMPL.hpp" 21 : #include "Utilities/TaggedTuple.hpp" 22 : 23 : /// \cond 24 : struct AllStepChoosers; 25 : class TimeDelta; 26 : class TimeStepId; 27 : namespace Parallel { 28 : template <typename Metavariables> 29 : class GlobalCache; 30 : } // namespace Parallel 31 : namespace StepChooserUse { 32 : struct LtsStep; 33 : } // namespace StepChooserUse 34 : namespace Tags { 35 : template <typename Tag> 36 : struct Next; 37 : struct StepChoosers; 38 : struct TimeStep; 39 : struct TimeStepId; 40 : template <typename StepperInterface> 41 : struct TimeStepper; 42 : } // namespace Tags 43 : // IWYU pragma: no_forward_declare db::DataBox 44 : /// \endcond 45 : 46 : /// \brief Adjust the step size for local time stepping, returning true if the 47 : /// step just completed is accepted, and false if it is rejected. 48 : /// 49 : /// \details The optional template parameter `StepChoosersToUse` may be used to 50 : /// indicate a subset of the constructable step choosers to use for the current 51 : /// application of `ChangeStepSize`. Passing `AllStepChoosers` (default) 52 : /// indicates that any constructible step chooser may be used. This option is 53 : /// used when multiple components need to invoke `ChangeStepSize` with step 54 : /// choosers that may not be compatible with all components. 55 : template <typename StepChoosersToUse = AllStepChoosers, typename DbTags> 56 1 : bool change_step_size(const gsl::not_null<db::DataBox<DbTags>*> box) { 57 : const LtsTimeStepper& time_stepper = 58 : db::get<Tags::TimeStepper<LtsTimeStepper>>(*box); 59 : const auto& step_choosers = db::get<Tags::StepChoosers>(*box); 60 : 61 : const auto& time_step_id = db::get<Tags::TimeStepId>(*box); 62 : using history_tags = ::Tags::get_all_history_tags<DbTags>; 63 : bool can_change_step_size = true; 64 : tmpl::for_each<history_tags>([&box, &can_change_step_size, &time_stepper, 65 : &time_step_id](auto tag_v) { 66 : if (not can_change_step_size) { 67 : return; 68 : } 69 : using tag = typename decltype(tag_v)::type; 70 : const auto& history = db::get<tag>(*box); 71 : can_change_step_size = 72 : time_stepper.can_change_step_size(time_step_id, history); 73 : }); 74 : if (not can_change_step_size) { 75 : return true; 76 : } 77 : 78 : const auto& current_step = db::get<Tags::TimeStep>(*box); 79 : 80 : const double last_step_size = std::abs(db::get<Tags::TimeStep>(*box).value()); 81 : 82 : // The step choosers return the magnitude of the desired step, so 83 : // we always want the minimum requirement, but we have to negate 84 : // the final answer if time is running backwards. 85 : double desired_step = std::numeric_limits<double>::infinity(); 86 : bool step_accepted = true; 87 : for (const auto& step_chooser : step_choosers) { 88 : const auto [step_choice, step_choice_accepted] = 89 : step_chooser->template desired_step<StepChoosersToUse>( 90 : last_step_size, *box); 91 : desired_step = std::min(desired_step, step_choice); 92 : step_accepted = step_accepted and step_choice_accepted; 93 : } 94 : if (not current_step.is_positive()) { 95 : desired_step = -desired_step; 96 : } 97 : 98 : if (abs(desired_step / current_step.slab().duration().value()) < 1.0e-9) { 99 : ERROR( 100 : "Chosen step is extremely small; this can indicate a flaw in the a " 101 : "step chooser, the grid, or a simualtion instability that an " 102 : "error-based stepper is naively attempting to resolve. It is unlikely " 103 : "that the simulation can proceed"); 104 : } 105 : 106 : const auto& next_time_id = db::get<Tags::Next<Tags::TimeStepId>>(*box); 107 : const auto new_step = 108 : choose_lts_step_size(next_time_id.step_time(), desired_step); 109 : db::mutate<Tags::Next<Tags::TimeStep>>( 110 : [&new_step](const gsl::not_null<TimeDelta*> next_step) { 111 : *next_step = new_step; 112 : }, 113 : box); 114 : // if step accepted, just proceed. Otherwise, change Time::Next and jump 115 : // back to the first instance of `UpdateU`. 116 : if (step_accepted) { 117 : return true; 118 : } else { 119 : db::mutate<Tags::Next<Tags::TimeStepId>, Tags::TimeStep>( 120 : [&time_stepper, &desired_step, &time_step_id]( 121 : const gsl::not_null<TimeStepId*> local_next_time_id, 122 : const gsl::not_null<TimeDelta*> time_step) { 123 : *time_step = 124 : choose_lts_step_size(time_step_id.step_time(), desired_step); 125 : *local_next_time_id = 126 : time_stepper.next_time_id(time_step_id, *time_step); 127 : }, 128 : box); 129 : return false; 130 : } 131 : } 132 : 133 : namespace Actions { 134 : /// \ingroup ActionsGroup 135 : /// \ingroup TimeGroup 136 : /// \brief Adjust the step size for local time stepping 137 : /// 138 : /// \details The optional template parameter `StepChoosersToUse` may be used to 139 : /// indicate a subset of the constructable step choosers to use for the current 140 : /// application of `ChangeStepSize`. Passing `AllStepChoosers` (default) 141 : /// indicates that any constructible step chooser may be used. This option is 142 : /// used when multiple components need to invoke `ChangeStepSize` with step 143 : /// choosers that may not be compatible with all components. 144 : /// 145 : /// Uses: 146 : /// - DataBox: 147 : /// - Tags::StepChoosers 148 : /// - Tags::HistoryEvolvedVariables 149 : /// - Tags::TimeStep 150 : /// - Tags::TimeStepId 151 : /// - Tags::TimeStepper<LtsTimeStepper> 152 : /// 153 : /// DataBox changes: 154 : /// - Adds: nothing 155 : /// - Removes: nothing 156 : /// - Modifies: Tags::Next<Tags::TimeStepId>, Tags::TimeStep 157 : template <typename StepChoosersToUse = AllStepChoosers> 158 1 : struct ChangeStepSize { 159 : template <typename DbTags, typename... InboxTags, typename Metavariables, 160 : typename ArrayIndex, typename ActionList, 161 : typename ParallelComponent> 162 0 : static Parallel::iterable_action_return_t apply( 163 : db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/, 164 : const Parallel::GlobalCache<Metavariables>& /*cache*/, 165 : const ArrayIndex& /*array_index*/, const ActionList /*meta*/, 166 : const ParallelComponent* const /*meta*/) { 167 : static_assert( 168 : tmpl::any<ActionList, tt::is_a<Actions::UpdateU, tmpl::_1>>::value, 169 : "The ChangeStepSize action requires that you also use the UpdateU " 170 : "action to permit step-unwinding. If you are stepping within " 171 : "an action that is not UpdateU, consider using the take_step function " 172 : "to handle both stepping and step-choosing instead of the " 173 : "ChangeStepSize action."); 174 : const bool step_successful = 175 : change_step_size<StepChoosersToUse>(make_not_null(&box)); 176 : // We should update 177 : // AdaptiveSteppingDiagnostics::number_of_step_fraction_changes, 178 : // but with the inter-action step unwinding it's hard to tell 179 : // whether that happened. Most executables use take_step instead 180 : // of this action, anyway. 181 : if (step_successful) { 182 : return {Parallel::AlgorithmExecution::Continue, std::nullopt}; 183 : } else { 184 : db::mutate<Tags::AdaptiveSteppingDiagnostics>( 185 : [](const gsl::not_null<AdaptiveSteppingDiagnostics*> diags) { 186 : ++diags->number_of_step_rejections; 187 : }, 188 : make_not_null(&box)); 189 : return {Parallel::AlgorithmExecution::Continue, 190 : tmpl::index_if<ActionList, 191 : tt::is_a<Actions::UpdateU, tmpl::_1>>::value}; 192 : } 193 : } 194 : }; 195 : } // namespace Actions