Line data Source code
1 0 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : #pragma once
5 :
6 : #include "DataStructures/DataBox/DataBox.hpp"
7 : #include "DataStructures/DataBox/Item.hpp"
8 : #include "DataStructures/DataBox/TagTraits.hpp"
9 : #include "Utilities/CleanupRoutine.hpp"
10 : #include "Utilities/ErrorHandling/StaticAssert.hpp"
11 : #include "Utilities/Gsl.hpp"
12 : #include "Utilities/TMPL.hpp"
13 :
14 : /// \cond
15 : template <typename ComputeTagsList, typename DataBoxType>
16 : class ObservationBox;
17 : /// \endcond
18 :
19 : namespace Tags {
20 : /*!
21 : * \brief Tag used to retrieve the ObservationBox from the `get()` function
22 : *
23 : * The main use of this tag is to allow fetching the ObservationBox from itself.
24 : * The intended primary use case is for Events to be able to retrieve the
25 : * ObservationBox and then do runtime retrieval of tags to avoid computing
26 : * quantities that aren't needed.
27 : */
28 1 : struct ObservationBox {
29 : // Trick to get friend function declaration to compile but a const
30 : // NoSuchtype****& is rather useless
31 0 : using type = NoSuchType****;
32 : };
33 : } // namespace Tags
34 :
35 : /*!
36 : * \ingroup DataStructuresGroup
37 : * \brief Used for adding compute items to a `DataBox` without copying or moving
38 : * any data from the original `DataBox`
39 : *
40 : * The intended use-case for this class is during IO/observing where additional
41 : * compute tags are needed only for observation. The memory used by those
42 : * compute tags does not need to be persistent and so we'd like a light-weight
43 : * class to handle the on-demand computation.
44 : */
45 : template <typename DataBoxType, typename... ComputeTags>
46 1 : class ObservationBox<tmpl::list<ComputeTags...>, DataBoxType>
47 : : private db::detail::Item<ComputeTags>... {
48 : public:
49 : /// A list of all the compute item tags
50 1 : using compute_item_tags = tmpl::list<ComputeTags...>;
51 : /// A list of all tags
52 1 : using tags_list =
53 : tmpl::push_back<typename DataBoxType::tags_list, ComputeTags...>;
54 :
55 0 : ObservationBox() = default;
56 0 : ObservationBox(const ObservationBox& rhs) = default;
57 0 : ObservationBox& operator=(const ObservationBox& rhs) = default;
58 0 : ObservationBox(ObservationBox&& rhs) = default;
59 0 : ObservationBox& operator=(ObservationBox&& rhs) = default;
60 0 : ~ObservationBox() = default;
61 :
62 : /// Create an `ObservationBox` that can also retrieve things out of the
63 : /// `databox` passed in.
64 1 : explicit ObservationBox(gsl::not_null<DataBoxType*> databox);
65 :
66 : /// Retrieve the tag `Tag`, should be called by the free function db::get
67 : template <typename Tag>
68 1 : const auto& get() const;
69 :
70 : /// @{
71 : /// Retrieve the underlying DataBox.
72 1 : DataBoxType& databox() { return *databox_; }
73 1 : const DataBoxType& databox() const { return *databox_; }
74 : /// @}
75 :
76 : /// Reset all the compute items, forcing reevaluation.
77 1 : void reset();
78 :
79 : private:
80 : template <typename Tag>
81 0 : const auto& get_item() const {
82 : return static_cast<const db::detail::Item<Tag>&>(*this);
83 : }
84 :
85 : template <typename ComputeTag, typename... ArgumentTags>
86 0 : void evaluate_compute_item(tmpl::list<ArgumentTags...> /*meta*/) const;
87 :
88 : template <typename ReferenceTag, typename... ArgumentTags>
89 0 : const auto& get_reference_item(tmpl::list<ArgumentTags...> /*meta*/) const;
90 :
91 0 : gsl::not_null<DataBoxType*> databox_;
92 : };
93 :
94 : /*!
95 : * \ingroup DataStructuresGroup
96 : * \brief Retrieve a `Tag` from the `ObservationBox`.
97 : */
98 : template <typename Tag, typename DataBoxType, typename... ComputeTags>
99 1 : const auto& get(
100 : const ObservationBox<tmpl::list<ComputeTags...>, DataBoxType>& box) {
101 : return box.template get<Tag>();
102 : }
103 :
104 : /// \cond
105 : template <typename DataBoxType, typename... ComputeTags>
106 : ObservationBox<tmpl::list<ComputeTags...>, DataBoxType>::ObservationBox(
107 : const gsl::not_null<DataBoxType*> databox)
108 : : databox_(databox) {
109 : DEBUG_STATIC_ASSERT(
110 : (db::is_immutable_item_tag_v<ComputeTags> and ...),
111 : "All tags passed to ObservationBox must be compute tags.");
112 : }
113 : /// \endcond
114 :
115 : template <typename DataBoxType, typename... ComputeTags>
116 : template <typename Tag>
117 : const auto& ObservationBox<tmpl::list<ComputeTags...>, DataBoxType>::get()
118 : const {
119 : if constexpr (std::is_same_v<Tag, ::Tags::DataBox>) {
120 : return *databox_;
121 : } else if constexpr (std::is_same_v<Tag, ::Tags::ObservationBox>) {
122 : return *this;
123 : } else {
124 : DEBUG_STATIC_ASSERT(
125 : not db::detail::has_no_matching_tag_v<tags_list, Tag>,
126 : "Found no tags in the ObservationBox that match the tag "
127 : "being retrieved.");
128 : DEBUG_STATIC_ASSERT(
129 : db::detail::has_unique_matching_tag_v<tags_list, Tag>,
130 : "Found more than one tag in the ObservationBox that matches the tag "
131 : "being retrieved. This happens because more than one tag with the same "
132 : "base (class) tag was added to the ObservationBox, or because you add "
133 : "a compute tag that is already in the DataBox.");
134 :
135 : if constexpr (db::tag_is_retrievable_v<Tag, DataBoxType>) {
136 : return db::get<Tag>(*databox_);
137 : } else {
138 : using item_tag = db::detail::first_matching_tag<compute_item_tags, Tag>;
139 : if constexpr (db::detail::Item<item_tag>::item_type ==
140 : db::detail::ItemType::Reference) {
141 : return get_reference_item<item_tag>(typename item_tag::argument_tags{});
142 : } else {
143 : if (not get_item<item_tag>().evaluated()) {
144 : evaluate_compute_item<item_tag>(typename item_tag::argument_tags{});
145 : }
146 : if constexpr (tt::is_a_v<std::unique_ptr, typename item_tag::type>) {
147 : return *(get_item<item_tag>().get());
148 : } else {
149 : return get_item<item_tag>().get();
150 : }
151 : }
152 : }
153 : }
154 : }
155 :
156 : template <typename DataBoxType, typename... ComputeTags>
157 : void ObservationBox<tmpl::list<ComputeTags...>, DataBoxType>::reset() {
158 : tmpl::for_each<
159 : tmpl::filter<tmpl::list<ComputeTags...>, db::is_compute_tag<tmpl::_1>>>(
160 : [this](auto tag) {
161 : static_cast<db::detail::Item<tmpl::type_from<decltype(tag)>>&>(*this)
162 : .reset();
163 : });
164 : }
165 :
166 : template <typename DataBoxType, typename... ComputeTags>
167 : template <typename ComputeTag, typename... ArgumentTags>
168 : void ObservationBox<tmpl::list<ComputeTags...>, DataBoxType>::
169 : evaluate_compute_item(tmpl::list<ArgumentTags...> /*meta*/) const {
170 : get_item<ComputeTag>().evaluate(get<ArgumentTags>()...);
171 : }
172 :
173 : template <typename DataBoxType, typename... ComputeTags>
174 : template <typename ReferenceTag, typename... ArgumentTags>
175 : const auto&
176 : ObservationBox<tmpl::list<ComputeTags...>, DataBoxType>::get_reference_item(
177 : tmpl::list<ArgumentTags...> /*meta*/) const {
178 : return ReferenceTag::get(get<ArgumentTags>()...);
179 : }
180 :
181 : template <typename ComputeTagsList, typename DataBoxType>
182 0 : auto make_observation_box(const gsl::not_null<DataBoxType*> databox) {
183 : return ObservationBox<db::detail::expand_subitems<ComputeTagsList>,
184 : DataBoxType>{databox};
185 : }
186 :
187 : namespace observation_box_detail {
188 : template <typename DataBoxType, typename ComputeTagsList, typename... Args,
189 : typename F, typename... ArgumentTags>
190 : auto apply(F&& f, tmpl::list<ArgumentTags...> /*meta*/,
191 : const ObservationBox<ComputeTagsList, DataBoxType>& observation_box,
192 : Args&&... args) {
193 : if constexpr (db::detail::is_apply_callable_v<
194 : F,
195 : std::decay_t<decltype(
196 : get<ArgumentTags>(observation_box))>...,
197 : Args...>) {
198 : return std::decay_t<F>::apply(get<ArgumentTags>(observation_box)...,
199 : std::forward<Args>(args)...);
200 : } else if constexpr (::tt::is_callable_v<
201 : F,
202 : std::decay_t<decltype(get<ArgumentTags>(observation_box))>...,
203 : Args...>) {
204 : return std::forward<F>(f)(get<ArgumentTags>(observation_box)...,
205 : std::forward<Args>(args)...);
206 : } else {
207 : db::detail::error_function_not_callable<
208 : F, std::decay_t<decltype(get<ArgumentTags>(observation_box))>...,
209 : Args...>();
210 : }
211 : }
212 :
213 : template <typename DataBoxType, typename ComputeTagsList, typename... Args,
214 : typename F, typename... ReturnTags, typename... ArgumentTags>
215 : decltype(auto) mutate_apply(
216 : F&& f, tmpl::list<ReturnTags...> /*meta*/,
217 : tmpl::list<ArgumentTags...> /*meta*/,
218 : const gsl::not_null<ObservationBox<ComputeTagsList, DataBoxType>*>
219 : observation_box,
220 : Args&&... args) {
221 : const CleanupRoutine reset_items = [&]() {
222 : if constexpr (sizeof...(ReturnTags) != 0) {
223 : // Not ideal, but doing a more granular reset is not worth the
224 : // trouble.
225 : observation_box->reset();
226 : }
227 : };
228 : return db::mutate_apply<tmpl::list<ReturnTags...>, tmpl::list<>>(
229 : f, make_not_null(&observation_box->databox()),
230 : get<ArgumentTags>(*observation_box)..., std::forward<Args>(args)...);
231 : }
232 : } // namespace observation_box_detail
233 :
234 : /*!
235 : * \ingroup DataStructuresGroup
236 : * \brief Apply the function object `f` using its nested `argument_tags` list of
237 : * tags.
238 : */
239 : template <typename DataBoxType, typename ComputeTagsList, typename... Args,
240 : typename F>
241 1 : auto apply(F&& f,
242 : const ObservationBox<ComputeTagsList, DataBoxType>& observation_box,
243 : Args&&... args) {
244 : return observation_box_detail::apply(
245 : std::forward<F>(f), typename std::decay_t<F>::argument_tags{},
246 : observation_box, std::forward<Args>(args)...);
247 : }
248 :
249 : /*!
250 : * \ingroup DataStructuresGroup
251 : * \brief Apply the function object `f` using its nested `return_tags`
252 : * and `argument_tags` list of tags. Modifications are made to the
253 : * underlying DataBox.
254 : */
255 : /// @{
256 : template <typename DataBoxType, typename ComputeTagsList, typename... Args,
257 : typename F>
258 1 : auto mutate_apply(
259 : F&& f,
260 : const gsl::not_null<ObservationBox<ComputeTagsList, DataBoxType>*>
261 : observation_box,
262 : Args&&... args) {
263 : return observation_box_detail::mutate_apply(
264 : std::forward<F>(f), typename std::decay_t<F>::return_tags{},
265 : typename std::decay_t<F>::argument_tags{}, observation_box,
266 : std::forward<Args>(args)...);
267 : }
268 :
269 : template <typename ReturnTags, typename ArgumentTags, typename DataBoxType,
270 : typename ComputeTagsList, typename... Args, typename F>
271 1 : auto mutate_apply(
272 : F&& f,
273 : const gsl::not_null<ObservationBox<ComputeTagsList, DataBoxType>*>
274 : observation_box,
275 : Args&&... args) {
276 : return observation_box_detail::mutate_apply(std::forward<F>(f), ReturnTags{},
277 : ArgumentTags{}, observation_box,
278 : std::forward<Args>(args)...);
279 : }
280 : /// @}
|