Line data Source code
1 0 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : #pragma once
5 :
6 : #include <optional>
7 :
8 : #include "DataStructures/DataBox/PrefixHelpers.hpp"
9 : #include "DataStructures/DataBox/SubitemTag.hpp"
10 : #include "DataStructures/DataBox/Subitems.hpp"
11 : #include "DataStructures/DataBox/Tag.hpp"
12 : #include "DataStructures/Variables.hpp"
13 : #include "Options/String.hpp"
14 : #include "PointwiseFunctions/AnalyticData/Tags.hpp"
15 : #include "PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp"
16 : #include "Utilities/PrettyType.hpp"
17 : #include "Utilities/Serialization/Serialize.hpp"
18 : #include "Utilities/TMPL.hpp"
19 :
20 : namespace OptionTags {
21 : /// \ingroup OptionGroupsGroup
22 : /// Holds the `OptionTags::AnalyticSolution` option in the input file
23 1 : struct AnalyticSolutionGroup {
24 0 : static std::string name() { return "AnalyticSolution"; }
25 0 : static constexpr Options::String help =
26 : "Analytic solution used for the initial data and errors";
27 : };
28 :
29 : /// \ingroup OptionTagsGroup
30 : /// The analytic solution, with the type of the analytic solution set as the
31 : /// template parameter
32 : template <typename SolutionType>
33 1 : struct AnalyticSolution {
34 0 : static std::string name() { return pretty_type::name<SolutionType>(); }
35 0 : static constexpr Options::String help = "Options for the analytic solution";
36 0 : using type = SolutionType;
37 0 : using group = AnalyticSolutionGroup;
38 : };
39 : } // namespace OptionTags
40 :
41 : namespace Tags {
42 : /// \ingroup OptionTagsGroup
43 : /// The analytic solution, with the type of the analytic solution set as the
44 : /// template parameter
45 : template <typename SolutionType>
46 1 : struct AnalyticSolution : db::SimpleTag {
47 0 : using type = SolutionType;
48 0 : using option_tags = tmpl::list<::OptionTags::AnalyticSolution<SolutionType>>;
49 :
50 0 : static constexpr bool pass_metavariables = false;
51 0 : static SolutionType create_from_options(
52 : const SolutionType& analytic_solution) {
53 : return serialize_and_deserialize<type>(analytic_solution);
54 : }
55 : };
56 :
57 : /// \ingroup DataBoxTagsGroup
58 : /// \brief Prefix indicating the analytic solution value for a quantity
59 : ///
60 : /// \snippet AnalyticSolutions/Test_Tags.cpp analytic_name
61 : template <typename Tag>
62 1 : struct Analytic : db::PrefixTag, db::SimpleTag {
63 0 : using type = std::optional<typename Tag::type>;
64 0 : using tag = Tag;
65 : };
66 :
67 : namespace detail {
68 : template <typename Tag>
69 : struct AnalyticImpl : db::PrefixTag, db::SimpleTag {
70 : using type = typename Tag::type;
71 : using tag = Tag;
72 : };
73 : } // namespace detail
74 :
75 : /*!
76 : * \brief The analytic solution of the `FieldTags`.
77 : *
78 : * The `std::optional` is a `nullopt` if there is no analytic solution.
79 : *
80 : * The individual `FieldTags` are added as `Tags::Analytic<Tag>` which holds a
81 : * `std::optional<Tensor>`.
82 : */
83 : template <typename FieldTags>
84 1 : struct AnalyticSolutions : db::SimpleTag {
85 0 : using field_tags = FieldTags;
86 0 : using type = std::optional<
87 : ::Variables<db::wrap_tags_in<detail::AnalyticImpl, FieldTags>>>;
88 : };
89 :
90 : namespace detail {
91 : // Check if the argument is a `::Tags::AnalyticSolutions` template, or derived
92 : // from it
93 : template <typename FieldTags>
94 : constexpr std::true_type is_analytic_solutions(
95 : ::Tags::AnalyticSolutions<FieldTags>&&) {
96 : return {};
97 : }
98 :
99 : constexpr std::false_type is_analytic_solutions(...) { return {}; }
100 :
101 : template <typename Tag>
102 : static constexpr bool is_analytic_solutions_v =
103 : decltype(is_analytic_solutions(std::declval<Tag>()))::value;
104 : } // namespace detail
105 :
106 : /*!
107 : * \brief The error of the `Tag` defined as `numerical - analytic`.
108 : *
109 : * The `std::optional` is a `nullopt` if no error was able to be computed, e.g.
110 : * if there is no analytic solution to compare to.
111 : */
112 : template <typename Tag>
113 1 : struct Error : db::PrefixTag, db::SimpleTag {
114 0 : using type = std::optional<typename Tag::type>;
115 0 : using tag = Tag;
116 : };
117 :
118 : namespace detail {
119 : template <typename Tag>
120 : struct ErrorImpl : db::PrefixTag, db::SimpleTag {
121 : using type = typename Tag::type;
122 : using tag = Tag;
123 : };
124 : } // namespace detail
125 :
126 : /*!
127 : * \brief The error of the `FieldTags`, defined as `numerical - analytic`.
128 : *
129 : * The `std::optional` is a `nullopt` if no error was able to be computed, e.g.
130 : * if there is no analytic solution to compare to.
131 : *
132 : * The individual `FieldTags` are added as `Tags::Error<Tag>` which holds a
133 : * `std::optional<Tensor>`.
134 : */
135 : template <typename FieldTags>
136 1 : struct Errors : db::SimpleTag {
137 0 : using field_tags = FieldTags;
138 0 : using type = std::optional<
139 : ::Variables<db::wrap_tags_in<detail::ErrorImpl, FieldTags>>>;
140 : };
141 :
142 : /// \cond
143 : template <typename FieldTagsList>
144 : struct ErrorsCompute;
145 : /// \endcond
146 :
147 : /*!
148 : * \brief Compute tag for computing the error from the `Tags::Analytic` of the
149 : * `FieldTags`.
150 : *
151 : * The error is defined as `numerical - analytic`.
152 : *
153 : * We use individual `Tensor`s rather than `Variables` of the `FieldTags`
154 : * because not all `FieldTags` are always stored in the same `Variables`.
155 : * For example, in the Generalized Harmonic-GRMHD combined system, the analytic
156 : * variables for the GH system are part of the `evolved_variables_tag` while the
157 : * GRMHD analytic variables are part of the `primitive_variables_tag`. A similar
158 : * issue arises in some elliptic systems. The main drawback of having to use the
159 : * tensor-by-tensor implementation instead of a `Variables` implementation is
160 : * the added loop complexity. However, this is offset by the reduced code
161 : * duplication and flexibility.
162 : */
163 : template <typename... FieldTags>
164 1 : struct ErrorsCompute<tmpl::list<FieldTags...>>
165 : : Errors<tmpl::list<FieldTags...>>, db::ComputeTag {
166 0 : using field_tags = tmpl::list<FieldTags...>;
167 0 : using base = Errors<tmpl::list<FieldTags...>>;
168 0 : using return_type = typename base::type;
169 0 : using argument_tags =
170 : tmpl::list<FieldTags..., ::Tags::Analytic<FieldTags>...>;
171 0 : static void function(
172 : const gsl::not_null<return_type*> errors,
173 : const typename FieldTags::type&... vars,
174 : const typename ::Tags::Analytic<FieldTags>::type&... analytic_vars) {
175 : if (const auto& first_analytic_var = get_first_argument(analytic_vars...);
176 : first_analytic_var.has_value()) {
177 : // Construct a Variables of the size of the DataVector of the first
178 : // analytic_vars tensor.
179 : *errors = typename return_type::value_type{
180 : first_analytic_var.value()[0].size()};
181 : const auto compute_error = [](const auto error_ptr, const auto& var,
182 : const auto& analytic_var) {
183 : for (size_t tensor_index = 0; tensor_index < var.size();
184 : ++tensor_index) {
185 : (*error_ptr)[tensor_index] =
186 : var[tensor_index] - analytic_var[tensor_index];
187 : }
188 : };
189 : EXPAND_PACK_LEFT_TO_RIGHT(compute_error(
190 : make_not_null(
191 : &get<::Tags::detail::ErrorImpl<FieldTags>>(errors->value())),
192 : vars, analytic_vars.value()));
193 : } else {
194 : *errors = std::nullopt;
195 : }
196 : }
197 : };
198 :
199 : namespace detail {
200 : // Check if the argument is a `::Tags::Errors` template, or derived
201 : // from it
202 : template <typename FieldTags>
203 : constexpr std::true_type is_errors(::Tags::Errors<FieldTags>&&) {
204 : return {};
205 : }
206 :
207 : constexpr std::false_type is_errors(...) { return {}; }
208 :
209 : template <typename Tag>
210 : static constexpr bool is_errors_v =
211 : decltype(is_errors(std::declval<Tag>()))::value;
212 : } // namespace detail
213 : } // namespace Tags
214 :
215 : /// \cond
216 : namespace Tags {
217 : template <typename Tag, typename ParentTag>
218 : struct Subitem<Tag, ParentTag,
219 : Requires<detail::is_analytic_solutions_v<ParentTag>>>
220 : : Tag, db::ComputeTag {
221 : using field_tags = typename ParentTag::field_tags;
222 : using base = Tag;
223 : using DataType = typename Tag::tag::type::type;
224 : using parent_tag = AnalyticSolutions<field_tags>;
225 : using argument_tags = tmpl::list<parent_tag>;
226 : static void function(const gsl::not_null<typename base::type*> result,
227 : const typename parent_tag::type& vars) {
228 : if (vars.has_value()) {
229 : result->reset();
230 : *result = typename base::type::value_type{};
231 : const auto& tensor =
232 : get<typename detail::AnalyticImpl<typename Tag::tag>>(*vars);
233 : const size_t num_components = tensor.size();
234 : for (size_t storage_index = 0; storage_index < num_components;
235 : ++storage_index) {
236 : (**result)[storage_index].set_data_ref(
237 : // NOLINTNEXTLINE
238 : make_not_null(&const_cast<DataType&>(tensor[storage_index])));
239 : }
240 : } else {
241 : *result = std::nullopt;
242 : }
243 : }
244 : };
245 :
246 : template <typename Tag, typename ParentTag>
247 : struct Subitem<Tag, ParentTag, Requires<detail::is_errors_v<ParentTag>>>
248 : : Tag, db::ComputeTag {
249 : using field_tags = typename ParentTag::field_tags;
250 : using base = Tag;
251 : using DataType = typename Tag::tag::type::type;
252 : using parent_tag = Errors<field_tags>;
253 : using argument_tags = tmpl::list<parent_tag>;
254 : static void function(const gsl::not_null<typename base::type*> result,
255 : const typename parent_tag::type& vars) {
256 : if (vars.has_value()) {
257 : result->reset();
258 : *result = typename base::type::value_type{};
259 : const auto& tensor =
260 : get<typename detail::ErrorImpl<typename Tag::tag>>(*vars);
261 : const size_t num_components = tensor.size();
262 : for (size_t storage_index = 0; storage_index < num_components;
263 : ++storage_index) {
264 : (**result)[storage_index].set_data_ref(
265 : // NOLINTNEXTLINE
266 : make_not_null(&const_cast<DataType&>(tensor[storage_index])));
267 : }
268 : } else {
269 : *result = std::nullopt;
270 : }
271 : }
272 : };
273 : } // namespace Tags
274 :
275 : namespace db {
276 : template <typename Tag>
277 : struct Subitems<Tag, Requires<Tags::detail::is_analytic_solutions_v<Tag>>> {
278 : using field_tags = typename Tag::field_tags;
279 : using type = db::wrap_tags_in<::Tags::Analytic, field_tags>;
280 :
281 : template <typename Subtag>
282 : static void create_item(
283 : const gsl::not_null<typename Tag::type*> parent_value,
284 : const gsl::not_null<typename Subtag::type*> sub_value) {
285 : if (parent_value->has_value()) {
286 : auto& tensor = get<::Tags::detail::AnalyticImpl<typename Subtag::tag>>(
287 : parent_value->value());
288 : sub_value->emplace();
289 : // Only update the Tensor if the Variables has changed its allocation
290 : if constexpr (not is_any_spin_weighted_v<
291 : typename Subtag::tag::type::type>) {
292 : if (tensor.begin()->data() != sub_value->value().begin()->data()) {
293 : for (auto tensor_component = tensor.begin(),
294 : sub_var_it = sub_value->value().begin();
295 : tensor_component != tensor.end();
296 : ++tensor_component, ++sub_var_it) {
297 : sub_var_it->set_data_ref(make_not_null(&*tensor_component));
298 : }
299 : }
300 : } else {
301 : if (tensor.begin()->data().data() !=
302 : sub_value->value().begin()->data().data()) {
303 : for (auto tensor_component = tensor.begin(),
304 : sub_var_it = sub_value->value().begin();
305 : tensor_component != tensor.end();
306 : ++tensor_component, ++sub_var_it) {
307 : sub_var_it->set_data_ref(make_not_null(&*tensor_component));
308 : }
309 : }
310 : }
311 : } else {
312 : *sub_value = std::nullopt;
313 : }
314 : }
315 : };
316 :
317 : template <typename Tag>
318 : struct Subitems<Tag, Requires<Tags::detail::is_errors_v<Tag>>> {
319 : using field_tags = typename Tag::field_tags;
320 : using type = db::wrap_tags_in<::Tags::Error, field_tags>;
321 :
322 : template <typename Subtag>
323 : static void create_item(
324 : const gsl::not_null<typename Tag::type*> parent_value,
325 : const gsl::not_null<typename Subtag::type*> sub_value) {
326 : if (parent_value->has_value()) {
327 : auto& tensor = get<::Tags::detail::ErrorImpl<typename Subtag::tag>>(
328 : parent_value->value());
329 : *sub_value = std::decay_t<decltype(tensor)>{};
330 : // Only update the Tensor if the Variables has changed its allocation
331 : if constexpr (not is_any_spin_weighted_v<
332 : typename Subtag::tag::type::type>) {
333 : if (tensor.begin()->data() != sub_value->value().begin()->data()) {
334 : for (auto tensor_component = tensor.begin(),
335 : sub_var_it = sub_value->value().begin();
336 : tensor_component != tensor.end();
337 : ++tensor_component, ++sub_var_it) {
338 : sub_var_it->set_data_ref(make_not_null(&*tensor_component));
339 : }
340 : }
341 : } else {
342 : if (tensor.begin()->data().data() !=
343 : sub_value->value().begin()->data().data()) {
344 : for (auto tensor_component = tensor.begin(),
345 : sub_var_it = sub_value->value().begin();
346 : tensor_component != tensor.end();
347 : ++tensor_component, ++sub_var_it) {
348 : sub_var_it->set_data_ref(make_not_null(&*tensor_component));
349 : }
350 : }
351 : }
352 : } else {
353 : *sub_value = std::nullopt;
354 : }
355 : }
356 : };
357 : } // namespace db
358 : /// \endcond
|