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 : /// @}
|