SpECTRE Documentation Coverage Report
Current view: top level - Domain - InterfaceHelpers.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 42.9 % 7 3
Test Date: 2026-10-22 17:29:42
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 <cstddef>
       7              : 
       8              : #include "DataStructures/DataBox/DataBox.hpp"
       9              : #include "DataStructures/DataBox/DataBoxTag.hpp"
      10              : #include "Domain/Structure/Direction.hpp"
      11              : #include "Domain/Structure/DirectionMap.hpp"
      12              : #include "Domain/Tags.hpp"
      13              : #include "Utilities/TMPL.hpp"
      14              : #include "Utilities/TypeTraits/CreateIsCallable.hpp"
      15              : 
      16              : namespace InterfaceHelpers_detail {
      17              : 
      18              : template <typename T, typename = std::void_t<>>
      19              : struct get_volume_tags_impl {
      20              :   using type = tmpl::list<>;
      21              : };
      22              : template <typename T>
      23              : struct get_volume_tags_impl<T, std::void_t<typename T::volume_tags>> {
      24              :   using type = typename T::volume_tags;
      25              : };
      26              : 
      27              : }  // namespace InterfaceHelpers_detail
      28              : 
      29              : /// Retrieve `T::volume_tags`, defaulting to an empty list
      30              : template <typename T>
      31            1 : using get_volume_tags =
      32              :     tmpl::type_from<InterfaceHelpers_detail::get_volume_tags_impl<T>>;
      33              : 
      34              : template <typename Tag, typename DirectionsTag, typename VolumeTags>
      35            0 : struct make_interface_tag {
      36            0 :   using type = tmpl::conditional_t<tmpl::list_contains_v<VolumeTags, Tag>, Tag,
      37              :                                    domain::Tags::Interface<DirectionsTag, Tag>>;
      38              : };
      39              : 
      40              : template <typename TagsList, typename DirectionsTag,
      41              :           typename VolumeTags = tmpl::list<>>
      42            0 : using make_interface_tags =
      43              :     tmpl::transform<TagsList,
      44              :                     make_interface_tag<tmpl::_1, tmpl::pin<DirectionsTag>,
      45              :                                        tmpl::pin<VolumeTags>>>;
      46              : 
      47              : namespace InterfaceHelpers_detail {
      48              : 
      49              : // Retrieve the `argument_tags` from the `InterfaceInvokable` and wrap them in
      50              : // `::Tags::Interface` if they are not listed in
      51              : // `InterfaceInvokable::volume_tags`.
      52              : template <typename InterfaceInvokable, typename DirectionsTag>
      53              : using get_interface_argument_tags = tmpl::transform<
      54              :     typename InterfaceInvokable::argument_tags,
      55              :     make_interface_tag<tmpl::_1, tmpl::pin<DirectionsTag>,
      56              :                        tmpl::pin<get_volume_tags<InterfaceInvokable>>>>;
      57              : 
      58              : /// Pull the direction's entry from interface arguments, passing volume
      59              : /// arguments through unchanged.
      60              : template <bool IsVolumeTag>
      61              : struct unmap_interface_args;
      62              : 
      63              : template <>
      64              : struct unmap_interface_args<true> {
      65              :   template <typename T>
      66              :   using f = T;
      67              : 
      68              :   template <size_t VolumeDim, typename T>
      69              :   static constexpr const T& apply(const ::Direction<VolumeDim>& /*direction*/,
      70              :                                   const T& arg) {
      71              :     return arg;
      72              :   }
      73              : };
      74              : 
      75              : template <>
      76              : struct unmap_interface_args<false> {
      77              :   template <typename T>
      78              :   using f = typename T::mapped_type;
      79              : 
      80              :   template <size_t VolumeDim, typename T>
      81              :   static constexpr decltype(auto) apply(const ::Direction<VolumeDim>& direction,
      82              :                                         const T& arg) {
      83              :     return arg.at(direction);
      84              :   }
      85              : };
      86              : 
      87              : CREATE_IS_CALLABLE(apply)
      88              : CREATE_IS_CALLABLE_V(apply)
      89              : 
      90              : template <bool HasStaticApply, typename InterfaceReturnType,
      91              :           typename DirectionsTag, typename VolumeTagsList,
      92              :           typename... ArgumentTags>
      93              : struct DispatchInterfaceInvokable;
      94              : 
      95              : template <typename InterfaceReturnType, typename DirectionsTag,
      96              :           typename VolumeTagsList, typename... ArgumentTags>
      97              : struct DispatchInterfaceInvokable<true, InterfaceReturnType, DirectionsTag,
      98              :                                   VolumeTagsList, ArgumentTags...> {
      99              :   template <typename InterfaceInvokable, typename DbTagsList,
     100              :             typename... ExtraArgs>
     101              :   static constexpr auto apply(InterfaceInvokable&& /*interface_invokable*/,
     102              :                               const db::DataBox<DbTagsList>& box,
     103              :                               ExtraArgs&&... extra_args) {
     104              :     DirectionMap<DirectionsTag::volume_dim, InterfaceReturnType> result{};
     105              :     for (const auto& direction : get<DirectionsTag>(box)) {
     106              :       auto interface_value = InterfaceInvokable::apply(
     107              :           unmap_interface_args<
     108              :               tmpl::list_contains_v<VolumeTagsList, ArgumentTags>>::
     109              :               apply(direction,
     110              :                     get<tmpl::type_from<make_interface_tag<
     111              :                         ArgumentTags, DirectionsTag, VolumeTagsList>>>(box))...,
     112              :           extra_args...);
     113              :       result.insert({direction, std::move(interface_value)});
     114              :     }
     115              :     return result;
     116              :   }
     117              : };
     118              : 
     119              : template <typename DirectionsTag, typename VolumeTagsList,
     120              :           typename... ArgumentTags>
     121              : struct DispatchInterfaceInvokable<true, void, DirectionsTag, VolumeTagsList,
     122              :                                   ArgumentTags...> {
     123              :   template <typename InterfaceInvokable, typename DbTagsList,
     124              :             typename... ExtraArgs>
     125              :   static constexpr void apply(InterfaceInvokable&& /*interface_invokable*/,
     126              :                               const db::DataBox<DbTagsList>& box,
     127              :                               ExtraArgs&&... extra_args) {
     128              :     for (const auto& direction : get<DirectionsTag>(box)) {
     129              :       InterfaceInvokable::apply(
     130              :           unmap_interface_args<
     131              :               tmpl::list_contains_v<VolumeTagsList, ArgumentTags>>::
     132              :               apply(direction,
     133              :                     get<tmpl::type_from<make_interface_tag<
     134              :                         ArgumentTags, DirectionsTag, VolumeTagsList>>>(box))...,
     135              :           extra_args...);
     136              :     }
     137              :   }
     138              : };
     139              : 
     140              : template <typename InterfaceReturnType, typename DirectionsTag,
     141              :           typename VolumeTagsList, typename... ArgumentTags>
     142              : struct DispatchInterfaceInvokable<false, InterfaceReturnType, DirectionsTag,
     143              :                                   VolumeTagsList, ArgumentTags...> {
     144              :   template <typename InterfaceInvokable, typename DbTagsList,
     145              :             typename... ExtraArgs>
     146              :   static constexpr auto apply(InterfaceInvokable&& interface_invokable,
     147              :                               const db::DataBox<DbTagsList>& box,
     148              :                               ExtraArgs&&... extra_args) {
     149              :     DirectionMap<DirectionsTag::volume_dim, InterfaceReturnType> result{};
     150              :     for (const auto& direction : get<DirectionsTag>(box)) {
     151              :       auto interface_value = interface_invokable(
     152              :           unmap_interface_args<
     153              :               tmpl::list_contains_v<VolumeTagsList, ArgumentTags>>::
     154              :               apply(direction,
     155              :                     get<tmpl::type_from<make_interface_tag<
     156              :                         ArgumentTags, DirectionsTag, VolumeTagsList>>>(box))...,
     157              :           extra_args...);
     158              :       result.insert({direction, std::move(interface_value)});
     159              :     }
     160              :     return result;
     161              :   }
     162              : };
     163              : 
     164              : template <typename DirectionsTag, typename VolumeTagsList,
     165              :           typename... ArgumentTags>
     166              : struct DispatchInterfaceInvokable<false, void, DirectionsTag, VolumeTagsList,
     167              :                                   ArgumentTags...> {
     168              :   template <typename InterfaceInvokable, typename DbTagsList,
     169              :             typename... ExtraArgs>
     170              :   static constexpr void apply(InterfaceInvokable&& interface_invokable,
     171              :                               const db::DataBox<DbTagsList>& box,
     172              :                               ExtraArgs&&... extra_args) {
     173              :     for (const auto& direction : get<DirectionsTag>(box)) {
     174              :       interface_invokable(
     175              :           unmap_interface_args<
     176              :               tmpl::list_contains_v<VolumeTagsList, ArgumentTags>>::
     177              :               apply(direction,
     178              :                     get<tmpl::type_from<make_interface_tag<
     179              :                         ArgumentTags, DirectionsTag, VolumeTagsList>>>(box))...,
     180              :           extra_args...);
     181              :     }
     182              :   }
     183              : };
     184              : 
     185              : template <typename DirectionsTag, typename ArgumentTags, typename VolumeTags>
     186              : struct InterfaceApplyImpl;
     187              : 
     188              : template <typename DirectionsTag, typename VolumeTagsList,
     189              :           typename... ArgumentTags>
     190              : struct InterfaceApplyImpl<DirectionsTag, tmpl::list<ArgumentTags...>,
     191              :                           VolumeTagsList> {
     192              :   static constexpr size_t volume_dim = DirectionsTag::volume_dim;
     193              : 
     194              :   template <
     195              :       typename InterfaceInvokable, typename DbTagsList, typename... ExtraArgs,
     196              :       Requires<is_apply_callable_v<
     197              :           InterfaceInvokable, db::const_item_type<ArgumentTags, DbTagsList>...,
     198              :           ExtraArgs...>> = nullptr>
     199              :   static constexpr auto apply(InterfaceInvokable&& interface_invokable,
     200              :                               const db::DataBox<DbTagsList>& box,
     201              :                               ExtraArgs&&... extra_args) {
     202              :     using interface_return_type =
     203              :         std::decay_t<decltype(InterfaceInvokable::apply(
     204              :             std::declval<db::const_item_type<ArgumentTags, DbTagsList>>()...,
     205              :             std::declval<ExtraArgs&&>()...))>;
     206              :     return DispatchInterfaceInvokable<true, interface_return_type,
     207              :                                       DirectionsTag, VolumeTagsList,
     208              :                                       ArgumentTags...>::
     209              :         apply(std::forward<InterfaceInvokable>(interface_invokable), box,
     210              :               std::forward<ExtraArgs>(extra_args)...);
     211              :   }
     212              : 
     213              :   template <
     214              :       typename InterfaceInvokable, typename DbTagsList, typename... ExtraArgs,
     215              :       Requires<not is_apply_callable_v<
     216              :           InterfaceInvokable, db::const_item_type<ArgumentTags, DbTagsList>...,
     217              :           ExtraArgs...>> = nullptr>
     218              :   static constexpr auto apply(InterfaceInvokable&& interface_invokable,
     219              :                               const db::DataBox<DbTagsList>& box,
     220              :                               ExtraArgs&&... extra_args) {
     221              :     using interface_return_type = std::decay_t<decltype(interface_invokable(
     222              :         std::declval<db::const_item_type<ArgumentTags, DbTagsList>>()...,
     223              :         std::declval<ExtraArgs&&>()...))>;
     224              :     return DispatchInterfaceInvokable<false, interface_return_type,
     225              :                                       DirectionsTag, VolumeTagsList,
     226              :                                       ArgumentTags...>::
     227              :         apply(std::forward<InterfaceInvokable>(interface_invokable), box,
     228              :               std::forward<ExtraArgs>(extra_args)...);
     229              :   }
     230              : };
     231              : 
     232              : }  // namespace InterfaceHelpers_detail
     233              : 
     234              : /// @{
     235              : /*!
     236              :  * \brief Apply an invokable to the `box` on all interfaces given by the
     237              :  * `DirectionsTag`.
     238              :  *
     239              :  * This function has an overload that takes an `interface_invokable` as its
     240              :  * first argument, and one that takes an `InterfaceInvokable` class as its first
     241              :  * template parameter instead. For the latter, the `InterfaceInvokable` class
     242              :  * must have a static `apply` function. The `interface_invokable` or static
     243              :  * `apply` function, respectively, is expected to be invokable with the types
     244              :  * held by the `ArgumentTags`, followed by the `extra_args`. The `ArgumentTags`
     245              :  * will be prefixed as `::Tags::Interface<DirectionsTag, ArgumentTag>` and thus
     246              :  * taken from the interface, except for those specified in the `VolumeTags`.
     247              :  *
     248              :  * This function returns a `DirectionMap` that holds the value returned by
     249              :  * the `interface_invokable` in every direction of the `DirectionsTag`.
     250              :  *
     251              :  * Here is an example how to use this function:
     252              :  *
     253              :  * \snippet Test_InterfaceHelpers.cpp interface_apply_example
     254              :  *
     255              :  * Here is another example how to use this function with a stateless invokable
     256              :  * class:
     257              :  *
     258              :  * \snippet Test_InterfaceHelpers.cpp interface_apply_example_stateless
     259              :  *
     260              :  * This is the class that defines the invokable in the example above:
     261              :  *
     262              :  * \snippet Test_InterfaceHelpers.cpp interface_invokable_example
     263              :  */
     264              : template <typename DirectionsTag, typename ArgumentTags, typename VolumeTags,
     265              :           typename InterfaceInvokable, typename DbTagsList,
     266              :           typename... ExtraArgs>
     267            1 : SPECTRE_ALWAYS_INLINE constexpr auto interface_apply(
     268              :     InterfaceInvokable&& interface_invokable,
     269              :     const db::DataBox<DbTagsList>& box, ExtraArgs&&... extra_args) {
     270              :   return InterfaceHelpers_detail::
     271              :       InterfaceApplyImpl<DirectionsTag, ArgumentTags, VolumeTags>::apply(
     272              :           std::forward<InterfaceInvokable>(interface_invokable), box,
     273              :           std::forward<ExtraArgs>(extra_args)...);
     274              : }
     275              : 
     276              : // The `box` argument to this function overload is not constrained to
     277              : // `db::DataBox` to work around an issue with GCC <= 8.
     278              : //
     279              : // Details on the issue:
     280              : //
     281              : // GCC <= 8 tries to instantiate the `db::DataBox<DbTagsList>` template even
     282              : // when this function overload is _not_ SFINAE-selected. In that case the
     283              : // template parameter substitution for `DbTagsList` may contain base tags that
     284              : // causes errors with the `db::DataBox` template instantiation.
     285              : template <typename DirectionsTag, typename InterfaceInvokable,
     286              :           typename DataBoxType, typename... ExtraArgs,
     287              :           // Needed to disambiguate the overloads
     288              :           typename ArgumentTags = typename InterfaceInvokable::argument_tags>
     289            1 : SPECTRE_ALWAYS_INLINE constexpr auto interface_apply(
     290              :     const DataBoxType& box, ExtraArgs&&... extra_args) {
     291              :   return interface_apply<DirectionsTag, ArgumentTags,
     292              :                          get_volume_tags<InterfaceInvokable>>(
     293              :       InterfaceInvokable{}, box, std::forward<ExtraArgs>(extra_args)...);
     294              : }
     295              : /// @}
        

Generated by: LCOV version 2.0-1