Line data Source code
1 0 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : #pragma once
5 :
6 : #include <type_traits>
7 :
8 : #include "ImplicitSource.hpp"
9 : #include "ImplicitSourceJacobian.hpp"
10 : #include "Utilities/ProtocolHelpers.hpp"
11 : #include "Utilities/TMPL.hpp"
12 : #include "Utilities/TypeTraits/IsA.hpp"
13 :
14 : /// \cond
15 : class DataVector;
16 : template <typename X, typename Symm, typename IndexList>
17 : class Tensor;
18 : namespace Tags {
19 : template <typename Tag>
20 : struct Source;
21 : } // namespace Tags
22 : /// \endcond
23 :
24 : namespace imex::protocols {
25 : /// Protocol for an implicit sector of an IMEX system.
26 : ///
27 : /// An implicit sector describes the sources for one implicit solve
28 : /// performed during IMEX evolution. A system may have multiple
29 : /// implicit sectors, but they must be independent, i.e., their
30 : /// sources must not depend on any tensors in other sectors.
31 : ///
32 : /// Classes implementing this protocol must define:
33 : ///
34 : /// * a `tensors` type alias of tags for the variables to be solved for
35 : ///
36 : /// * an `initial_guess` type to be passed to `db::mutate_apply`,
37 : /// taking additional arguments for the inhomogeneous terms \f$X\f$
38 : /// and implicit weight \f$w\f$ in the equation to be solved:
39 : /// \f$u = X + w S(u)\f$. (See example below.) It must return a
40 : /// `std::vector<GuessResult>` indicating, for each point, whether
41 : /// the implicit equation has been solved analytically or whether
42 : /// the numerical solve should continue. An empty return is
43 : /// equivalent to `imex::GuessResult::InitialGuess` for every point,
44 : /// so numerical solves will be performed for each. When this is
45 : /// called, the sector variables will have the value from the
46 : /// explicit part of the time step. This mutator will not be called
47 : /// if the implicit weight is zero, as the solution is trivial in
48 : /// that case. If using the value of the explicit step as an
49 : /// initial guess is acceptable, this can be the type
50 : /// `imex::GuessExplicitResult`.
51 : ///
52 : /// * a `solve_attempts` list of sources that will be attempted to be
53 : /// solved, in order. The first one that succeeds at each point
54 : /// will be used. Pieces of code that need "the" source for the
55 : /// sector will use the source from the first entry. Each attempt
56 : /// must be a struct with the following types:
57 : ///
58 : /// * lists used to construct a DataBox during the pointwise
59 : /// implicit solve:
60 : ///
61 : /// * `tags_from_evolution` for tags in addition to the sector
62 : /// tensors to be made available from the evolution DataBox.
63 : /// Volume quantities will be reduced to have one grid point,
64 : /// with the appropriate value for the point being solved for.
65 : ///
66 : /// * `simple_tags` for temporaries (e.g., primitives)
67 : ///
68 : /// * `compute_tags`
69 : ///
70 : /// * a `source` type to be passed to `db::mutate_apply` to compute
71 : /// the sources. It must conform to imex::protocols::ImplicitSource.
72 : ///
73 : /// * a `jacobian` type to be passed to `db::mutate_apply` to
74 : /// compute the source jacobian. It must conform to
75 : /// imex::protocols::ImplicitSourceJacobian. If the implicit equation can
76 : /// always be solved analytically for the sector, the jacobian is not
77 : /// required and this may be the type
78 : /// `imex::NoJacobianBecauseSolutionIsAnalytic`.
79 : ///
80 : /// * lists `source_prep` and `jacobian_prep` that will be called
81 : /// before the corresponding main mutator, e.g., for computing
82 : /// primitives. Mutators appearing in multiple lists, as well as
83 : /// the `source` and `jacobian` mutators, will be skipped if they
84 : /// have already been applied for the current point. Note that
85 : /// the `source_prep` mutators are only used during the implicit
86 : /// solve, and any preparation needed before the `source` call in
87 : /// the main action loop to record the history is the
88 : /// responsibility of the user.
89 : ///
90 : /// All `Variables` in the DataBox, including the sources and source
91 : /// jacobian, will be initialized to zero with a single grid point.
92 : ///
93 : /// \snippet DoImplicitStepSector.hpp simple_sector
94 : ///
95 : /// Examples of definitions of a complicated implicit source and
96 : /// jacobian:
97 : ///
98 : /// \snippet Test_SolveImplicitSector.cpp source
99 : /// \snippet Test_SolveImplicitSector.cpp Jacobian
100 1 : struct ImplicitSector {
101 : template <typename ConformingType>
102 0 : struct test {
103 0 : using tensors = typename ConformingType::tensors;
104 0 : using initial_guess = typename ConformingType::initial_guess;
105 0 : using solve_attempts = typename ConformingType::solve_attempts;
106 :
107 : static_assert(tt::is_a_v<tmpl::list, tensors>);
108 : static_assert(tt::is_a_v<tmpl::list, solve_attempts>);
109 : static_assert(tmpl::size<solve_attempts>::value >= 1);
110 :
111 : static_assert(
112 : tmpl::all<
113 : tensors,
114 : tt::is_a<Tensor, tmpl::bind<tmpl::type_from, tmpl::_1>>>::value);
115 :
116 0 : using source_tensors =
117 : tmpl::transform<tensors, tmpl::bind<::Tags::Source, tmpl::_1>>;
118 :
119 : static_assert(
120 : tmpl::size<tmpl::list_difference<typename initial_guess::return_tags,
121 : tensors>>::value == 0,
122 : "initial_guess can only modify sector variables.");
123 :
124 : template <typename SolveAttempt>
125 0 : struct test_solve_attempt {
126 0 : using source = typename SolveAttempt::source;
127 0 : using jacobian = typename SolveAttempt::jacobian;
128 :
129 0 : using tags_from_evolution = typename SolveAttempt::tags_from_evolution;
130 0 : using simple_tags = typename SolveAttempt::simple_tags;
131 0 : using compute_tags = typename SolveAttempt::compute_tags;
132 :
133 0 : using source_prep = typename SolveAttempt::source_prep;
134 0 : using jacobian_prep = typename SolveAttempt::jacobian_prep;
135 :
136 : // check protocol conformity
137 : static_assert(
138 : tt::assert_conforms_to_v<source, imex::protocols::ImplicitSource>);
139 : static_assert(tt::assert_conforms_to_v<
140 : jacobian, imex::protocols::ImplicitSourceJacobian>);
141 :
142 : static_assert(tt::is_a_v<tmpl::list, tags_from_evolution>);
143 : static_assert(tt::is_a_v<tmpl::list, simple_tags>);
144 : static_assert(tt::is_a_v<tmpl::list, compute_tags>);
145 : static_assert(tt::is_a_v<tmpl::list, source_prep>);
146 : static_assert(tt::is_a_v<tmpl::list, jacobian_prep>);
147 :
148 : static_assert(
149 : std::is_same_v<tmpl::list_difference<tags_from_evolution, tensors>,
150 : tags_from_evolution>,
151 : "tags_from_evolution cannot include the sector tensors.");
152 :
153 : static_assert(
154 : std::is_same_v<tmpl::list_difference<source_tensors,
155 : typename source::return_tags>,
156 : tmpl::list<>> and
157 : std::is_same_v<tmpl::list_difference<typename source::return_tags,
158 : source_tensors>,
159 : tmpl::list<>>,
160 : "Implicit source must provide sources for the entire sector.");
161 :
162 : template <typename T>
163 0 : struct is_a_tensor_of_data_vector : std::false_type {};
164 :
165 : template <typename Symm, typename IndexList>
166 0 : struct is_a_tensor_of_data_vector<Tensor<DataVector, Symm, IndexList>>
167 : : std::true_type {};
168 :
169 : static_assert(
170 : tmpl::none<simple_tags, is_a_tensor_of_data_vector<tmpl::bind<
171 : tmpl::type_from, tmpl::_1>>>::value,
172 : "Do not include tags for Tensor<DataVector> in simple_tags, because "
173 : "they trigger many memory allocations. Add the tensors as part of "
174 : "a Variables instead.");
175 :
176 0 : using type = std::true_type;
177 : };
178 :
179 : static_assert(
180 : tmpl::all<solve_attempts, test_solve_attempt<tmpl::_1>>::value);
181 : };
182 : };
183 : } // namespace imex::protocols
|