Line data Source code
1 1 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : /// \file
5 : /// Defines DataBox tags for the linear solver
6 :
7 : #pragma once
8 :
9 : #include <cstddef>
10 : #include <string>
11 :
12 : #include "DataStructures/DataBox/PrefixHelpers.hpp"
13 : #include "DataStructures/DataBox/Tag.hpp"
14 : #include "Options/String.hpp"
15 : #include "Utilities/Gsl.hpp"
16 : #include "Utilities/PrettyType.hpp"
17 :
18 : /// Functionality for solving nonlinear systems of equations
19 : namespace NonlinearSolver {
20 :
21 : /// Options related to nonlinear solvers
22 1 : namespace OptionTags {
23 :
24 : /*!
25 : * \brief Sufficient decrease parameter of the line search globalization
26 : *
27 : * The sufficient decrease parameter is the acceptable decrease of the residual
28 : * magnitude in each step of the nonlinear solver. It is measured as a fraction
29 : * of the predicted decrease in residual magnitude if the problem was linear.
30 : * For example, a sufficient decrease parameter of 1 means that a nonlinear
31 : * solver step is expected to decrease the residual exactly as expected for a
32 : * linear problem, i.e. immediately to zero. A sufficient decrease parameter of
33 : * 0.5 means that decreasing the residual by half of that amount in each
34 : * nonlinear solver step is acceptable.
35 : *
36 : * Nonlinear solver steps that fail the sufficient decrease condition (also
37 : * known as _Armijo condition_) undergo a globalization procedure such as a line
38 : * search.
39 : *
40 : * A typical value for the sufficient decrease parameter is \f$10^{-4}\f$. Set
41 : * to values closer to unity when the nonlinear solver overshoots, e.g. when the
42 : * initial guess is particularly bad. Larger values mean the nonlinear solver is
43 : * stricter with accepting steps, preferring to apply the globalization
44 : * strategy.
45 : */
46 : template <typename OptionsGroup>
47 1 : struct SufficientDecrease {
48 0 : using type = double;
49 0 : static constexpr Options::String help = {
50 : "Fraction of decrease predicted by linearization"};
51 0 : static type lower_bound() { return 0.; }
52 0 : static type upper_bound() { return 1.; }
53 0 : static type suggested_value() { return 1.e-4; }
54 0 : using group = OptionsGroup;
55 : };
56 :
57 : /*!
58 : * \brief Nonlinear solver steps are damped by this factor
59 : *
60 : * Instead of attempting to take full-length steps when correcting the solution
61 : * in each nonlinear solver step (see `NonlinearSolver::Tags::Correction`),
62 : * reduce the step length by this factor. This damping occurs before any
63 : * globalization steps that may further reduce the step length.
64 : */
65 : template <typename OptionsGroup>
66 1 : struct DampingFactor {
67 0 : using type = double;
68 0 : static constexpr Options::String help = {
69 : "Multiply corrections by this factor"};
70 0 : static type lower_bound() { return 0.; }
71 0 : static type upper_bound() { return 1.; }
72 0 : static type suggested_value() { return 1.; }
73 0 : using group = OptionsGroup;
74 : };
75 :
76 : /*!
77 : * \brief The maximum number of allowed globalization steps
78 : *
79 : * Nonlinear solves of well-posed problems should never hit this limit because
80 : * the step size shrinks and eventually triggers the sufficient-decrease
81 : * condition (see `NonlinearSolver::OptionTags::SufficientDecrease`). So the
82 : * suggested value just provides a safety-net to prevent the globalization
83 : * from running forever when the problem is ill-posed.
84 : */
85 : template <typename OptionsGroup>
86 1 : struct MaxGlobalizationSteps {
87 0 : using type = size_t;
88 0 : static constexpr Options::String help = {
89 : "Maximum number of globalization steps"};
90 0 : static type suggested_value() { return 40; }
91 0 : using group = OptionsGroup;
92 : };
93 :
94 : } // namespace OptionTags
95 :
96 0 : namespace Tags {
97 :
98 : /*!
99 : * \brief The correction \f$\delta x\f$ to improve a solution \f$x_0\f$
100 : *
101 : * A linear problem \f$Ax=b\f$ can be equivalently formulated as the problem
102 : * \f$A\delta x=b-A x_0\f$ for the correction \f$\delta x\f$ to an initial guess
103 : * \f$x_0\f$. More importantly, we can use a correction scheme to solve a
104 : * nonlinear problem \f$A_\mathrm{nonlinear}(x)=b\f$ by repeatedly solving a
105 : * linearization of it. For instance, a Newton-Raphson scheme iteratively
106 : * refines an initial guess \f$x_0\f$ by repeatedly solving the linearized
107 : * problem
108 : *
109 : * \f{equation}
110 : * \frac{\delta A_\mathrm{nonlinear}}{\delta x}(x_k)\delta x_k =
111 : * b-A_\mathrm{nonlinear}(x_k)
112 : * \f}
113 : *
114 : * for the correction \f$\delta x_k\f$ and then updating the solution as
115 : * \f$x_{k+1}=x_k + \delta x_k\f$.
116 : */
117 : template <typename Tag>
118 1 : struct Correction : db::PrefixTag, db::SimpleTag {
119 0 : using type = typename Tag::type;
120 0 : using tag = Tag;
121 : };
122 :
123 : /*!
124 : * \brief The nonlinear operator \f$A_\mathrm{nonlinear}\f$ applied to the data
125 : * in `Tag`
126 : */
127 : template <typename Tag>
128 1 : struct OperatorAppliedTo : db::PrefixTag, db::SimpleTag {
129 0 : static std::string name() {
130 : // Add "Nonlinear" prefix to abbreviate the namespace for uniqueness
131 : return "NonlinearOperatorAppliedTo(" + db::tag_name<Tag>() + ")";
132 : }
133 0 : using type = typename Tag::type;
134 0 : using tag = Tag;
135 : };
136 :
137 : /*!
138 : * \brief The nonlinear residual
139 : * \f$r_\mathrm{nonlinear} = b - A_\mathrm{nonlinear}(\delta x)\f$
140 : */
141 : template <typename Tag>
142 1 : struct Residual : db::PrefixTag, db::SimpleTag {
143 0 : static std::string name() {
144 : // Add "Nonlinear" prefix to abbreviate the namespace for uniqueness
145 : return "NonlinearResidual(" + db::tag_name<Tag>() + ")";
146 : }
147 0 : using type = typename Tag::type;
148 0 : using tag = Tag;
149 : };
150 :
151 : /// Compute the residual \f$r=b - Ax\f$ from the `SourceTag` \f$b\f$ and the
152 : /// `db::add_tag_prefix<NonlinearSolver::Tags::OperatorAppliedTo, FieldsTag>`
153 : /// \f$Ax\f$.
154 : template <typename FieldsTag, typename SourceTag>
155 1 : struct ResidualCompute : db::add_tag_prefix<Residual, FieldsTag>,
156 : db::ComputeTag {
157 0 : using base = db::add_tag_prefix<Residual, FieldsTag>;
158 0 : using argument_tags =
159 : tmpl::list<SourceTag, db::add_tag_prefix<OperatorAppliedTo, FieldsTag>>;
160 0 : using return_type = typename base::type;
161 0 : static void function(
162 : const gsl::not_null<return_type*> residual,
163 : const typename SourceTag::type& source,
164 : const typename db::add_tag_prefix<OperatorAppliedTo, FieldsTag>::type&
165 : operator_applied_to_fields) {
166 : *residual = source - operator_applied_to_fields;
167 : }
168 : };
169 :
170 : /*!
171 : * \brief The length of nonlinear solver steps
172 : *
173 : * Instead of taking full-length nonlinear solver steps when correcting the
174 : * solution as detailed in `NonlinearSolver::Tags::Correction`, the correction
175 : * is multiplied by this step length.
176 : *
177 : * The `NonlinearSolver::Tags::DampingFactor` multiplies the initial length of
178 : * each step such that the nonlinear solver never takes full-length steps if the
179 : * damping factor is below one. The step length can be further reduced by the
180 : * globalization procedure. See `NonlinearSolver::NewtonRaphson` for details.
181 : */
182 : template <typename OptionsGroup>
183 1 : struct StepLength : db::SimpleTag {
184 0 : static std::string name() {
185 : return "StepLength(" + pretty_type::name<OptionsGroup>() + ")";
186 : }
187 0 : using type = double;
188 : };
189 :
190 : /*!
191 : * \brief Sufficient decrease parameter of the line search globalization
192 : *
193 : * \see `NonlinearSolver::OptionTags::SufficientDecrease`
194 : */
195 : template <typename OptionsGroup>
196 1 : struct SufficientDecrease : db::SimpleTag {
197 0 : static std::string name() {
198 : return "SufficientDecrease(" + pretty_type::name<OptionsGroup>() + ")";
199 : }
200 0 : using type = double;
201 0 : static constexpr bool pass_metavariables = false;
202 0 : using option_tags = tmpl::list<OptionTags::SufficientDecrease<OptionsGroup>>;
203 0 : static type create_from_options(const type& option) { return option; }
204 : };
205 :
206 : /*!
207 : * \brief Nonlinear solver steps are damped by this factor
208 : *
209 : * \see `NonlinearSolver::OptionTags::DampingFactor`
210 : */
211 : template <typename OptionsGroup>
212 1 : struct DampingFactor : db::SimpleTag {
213 0 : static std::string name() {
214 : return "DampingFactor(" + pretty_type::name<OptionsGroup>() + ")";
215 : }
216 0 : using type = double;
217 0 : static constexpr bool pass_metavariables = false;
218 0 : using option_tags = tmpl::list<OptionTags::DampingFactor<OptionsGroup>>;
219 0 : static type create_from_options(const type& option) { return option; }
220 : };
221 :
222 : /*!
223 : * \brief The maximum number of allowed globalization steps
224 : *
225 : * \see `NonlinearSolver::OptionTags::MinStepLength`
226 : */
227 : template <typename OptionsGroup>
228 1 : struct MaxGlobalizationSteps : db::SimpleTag {
229 0 : static std::string name() {
230 : return "MaxGlobalizationSteps(" + pretty_type::name<OptionsGroup>() + ")";
231 : }
232 0 : using type = size_t;
233 0 : static constexpr bool pass_metavariables = false;
234 0 : using option_tags =
235 : tmpl::list<OptionTags::MaxGlobalizationSteps<OptionsGroup>>;
236 0 : static type create_from_options(const type& option) { return option; }
237 : };
238 :
239 : /// Prefix indicating the `Tag` is related to the globalization procedure
240 : template <typename Tag>
241 1 : struct Globalization : db::PrefixTag, db::SimpleTag {
242 0 : using type = typename Tag::type;
243 0 : using tag = Tag;
244 : };
245 :
246 : } // namespace Tags
247 : } // namespace NonlinearSolver
|