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 : };
|