Line data Source code
1 0 : // Distributed under the MIT License. 2 : // See LICENSE.txt for details. 3 : 4 : // Tests for the VariableOrderAlgorithm class are performed in 5 : // Test_ChangeTimeStepperOrder.cpp. The class is split into this file 6 : // to avoid include loops with the associated tags. 7 : 8 : #pragma once 9 : 10 : #include <array> 11 : #include <cstddef> 12 : #include <optional> 13 : 14 : #include "Options/String.hpp" 15 : #include "Time/History.hpp" 16 : #include "Time/StepperErrorEstimate.hpp" 17 : #include "Time/StepperErrorTolerances.hpp" 18 : #include "Utilities/ErrorHandling/Assert.hpp" 19 : #include "Utilities/TMPL.hpp" 20 : 21 : /// \cond 22 : namespace Options { 23 : template <typename... AlternativeLists> 24 : struct Alternatives; 25 : } // namespace Options 26 : /// \endcond 27 : 28 : /*! 29 : * \ingroup TimeGroup 30 : * \brief Class encapsulating the time-stepper order changing algorithms 31 : * 32 : * \details Supports two modes: driving the order to a specified constant, and 33 : * measuring the convergence of the error estimates. 34 : * 35 : * When driving to a constant, the order is changed by one towards the 36 : * goal if it is not at the goal. 37 : * 38 : * When measuring convergence, let the relative error estimate from 39 : * the time stepper for order-$k$ integration be $e_k$, and $\lambda 40 : * \le 1$ be the falloff this class was constructed with. Then, we 41 : * decrease the order by one if, for any $k \ge 2$ and less than the 42 : * current integration order, 43 : * 44 : * \f{equation} 45 : * \frac{e_k}{e_{k-1}} > 46 : * \left(\min_{2 \le j < k} \frac{e_j}{e_{j-1}}\right)^\lambda, 47 : * \f} 48 : * 49 : * with the right-hand side taken to be $1$ for $k = 2$. If the order 50 : * is not decreased, it is kept the same if the above condition holds 51 : * for $k$ equal to the current integration order, and is increased by 52 : * one otherwise. If the current integration order is $1$ (not 53 : * possible for predictor-corrector methods), it is always increased. 54 : * This algorithm will almost never prefer an integration order less 55 : * than three. 56 : * 57 : * \note This is currently all implemented in one class for simplicity 58 : * with dealing with templates. If we add more algorithms splitting 59 : * into a base class with implementations would be appropriate. 60 : */ 61 1 : class VariableOrderAlgorithm { 62 : public: 63 0 : struct GoalOrder { 64 0 : using type = size_t; 65 0 : static constexpr Options::String help = "Order to drive the integrator to."; 66 : }; 67 : 68 0 : struct OrderFalloff { 69 0 : using type = double; 70 0 : static constexpr Options::String help = 71 : "Threshold for changing time-stepper order, as a logarithmic " 72 : "fraction of the best order-to-order improvement."; 73 : }; 74 : 75 0 : using options = tmpl::list< 76 : Options::Alternatives<tmpl::list<GoalOrder>, tmpl::list<OrderFalloff>>>; 77 0 : static constexpr Options::String help = 78 : "Algorithm for choosing the time-stepper order in a variable-order " 79 : "evolution."; 80 : 81 0 : VariableOrderAlgorithm(); 82 0 : explicit VariableOrderAlgorithm(size_t goal_order); 83 0 : explicit VariableOrderAlgorithm(double order_falloff); 84 : 85 0 : StepperErrorTolerances::Estimates required_estimates() const; 86 : 87 : template <typename... VariablesTags> 88 0 : size_t choose_order( 89 : const TimeSteppers::History<typename VariablesTags::type>&... histories, 90 : const typename tmpl::has_type< 91 : VariablesTags, 92 : std::array<std::optional<StepperErrorEstimate>, 2>>::type&... errors) 93 : const { 94 : const auto history_order = 95 : get_first_argument(histories...).integration_order(); 96 : ASSERT((... and (history_order == histories.integration_order())), 97 : "Multiple histories with different integration orders."); 98 : 99 : if (goal_order_.has_value()) { 100 : ASSERT(not order_falloff_.has_value(), 101 : "Internal error: should not have multiple algorithms active"); 102 : return choose_order_goal(history_order); 103 : } else { 104 : ASSERT(order_falloff_.has_value(), 105 : "VariableOrderAlgorithm not initialized"); 106 : return choose_order_falloff(history_order, std::array{&errors[1]...}); 107 : } 108 : } 109 : 110 0 : void pup(PUP::er& p); 111 : 112 : private: 113 0 : size_t choose_order_goal(size_t current_order) const; 114 : 115 : template <size_t NVars> 116 0 : size_t choose_order_falloff( 117 : size_t current_order, 118 : const std::array<const std::optional<StepperErrorEstimate>*, NVars>& 119 : errors) const; 120 : 121 0 : friend bool operator==(const VariableOrderAlgorithm& a, 122 : const VariableOrderAlgorithm& b); 123 : 124 0 : std::optional<size_t> goal_order_{}; 125 0 : std::optional<double> order_falloff_{}; 126 : };