SpECTRE Documentation Coverage Report
Current view: top level - PointwiseFunctions/AnalyticSolutions - Tags.hpp Hit Total Coverage
Commit: 1f2210958b4f38fdc0400907ee7c6d5af5111418 Lines: 8 32 25.0 %
Date: 2025-12-05 05:03:31
Legend: Lines: hit not hit

          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 deserialize<type>(serialize<type>(analytic_solution).data());
      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

Generated by: LCOV version 1.14