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 : /// Retrieve the underlying DataBox.
71 1 : DataBoxType& databox() { return *databox_; }
72 :
73 : /// Reset all the compute items, forcing reevaluation.
74 1 : void reset();
75 :
76 : private:
77 : template <typename Tag>
78 0 : const auto& get_item() const {
79 : return static_cast<const db::detail::Item<Tag>&>(*this);
80 : }
81 :
82 : template <typename ComputeTag, typename... ArgumentTags>
83 0 : void evaluate_compute_item(tmpl::list<ArgumentTags...> /*meta*/) const;
84 :
85 : template <typename ReferenceTag, typename... ArgumentTags>
86 0 : const auto& get_reference_item(tmpl::list<ArgumentTags...> /*meta*/) const;
87 :
88 0 : gsl::not_null<DataBoxType*> databox_;
89 : };
90 :
91 : /*!
92 : * \ingroup DataStructuresGroup
93 : * \brief Retrieve a `Tag` from the `ObservationBox`.
94 : */
95 : template <typename Tag, typename DataBoxType, typename... ComputeTags>
96 1 : const auto& get(
97 : const ObservationBox<tmpl::list<ComputeTags...>, DataBoxType>& box) {
98 : return box.template get<Tag>();
99 : }
100 :
101 : /// \cond
102 : template <typename DataBoxType, typename... ComputeTags>
103 : ObservationBox<tmpl::list<ComputeTags...>, DataBoxType>::ObservationBox(
104 : const gsl::not_null<DataBoxType*> databox)
105 : : databox_(databox) {
106 : DEBUG_STATIC_ASSERT(
107 : (db::is_immutable_item_tag_v<ComputeTags> and ...),
108 : "All tags passed to ObservationBox must be compute tags.");
109 : }
110 : /// \endcond
111 :
112 : template <typename DataBoxType, typename... ComputeTags>
113 : template <typename Tag>
114 : const auto& ObservationBox<tmpl::list<ComputeTags...>, DataBoxType>::get()
115 : const {
116 : if constexpr (std::is_same_v<Tag, ::Tags::DataBox>) {
117 : return *databox_;
118 : } else if constexpr (std::is_same_v<Tag, ::Tags::ObservationBox>) {
119 : return *this;
120 : } else {
121 : DEBUG_STATIC_ASSERT(
122 : not db::detail::has_no_matching_tag_v<tags_list, Tag>,
123 : "Found no tags in the ObservationBox that match the tag "
124 : "being retrieved.");
125 : DEBUG_STATIC_ASSERT(
126 : db::detail::has_unique_matching_tag_v<tags_list, Tag>,
127 : "Found more than one tag in the ObservationBox that matches the tag "
128 : "being retrieved. This happens because more than one tag with the same "
129 : "base (class) tag was added to the ObservationBox, or because you add "
130 : "a compute tag that is already in the DataBox.");
131 :
132 : if constexpr (db::tag_is_retrievable_v<Tag, DataBoxType>) {
133 : return db::get<Tag>(*databox_);
134 : } else {
135 : using item_tag = db::detail::first_matching_tag<compute_item_tags, Tag>;
136 : if constexpr (db::detail::Item<item_tag>::item_type ==
137 : db::detail::ItemType::Reference) {
138 : return get_reference_item<item_tag>(typename item_tag::argument_tags{});
139 : } else {
140 : if (not get_item<item_tag>().evaluated()) {
141 : evaluate_compute_item<item_tag>(typename item_tag::argument_tags{});
142 : }
143 : if constexpr (tt::is_a_v<std::unique_ptr, typename item_tag::type>) {
144 : return *(get_item<item_tag>().get());
145 : } else {
146 : return get_item<item_tag>().get();
147 : }
148 : }
149 : }
150 : }
151 : }
152 :
153 : template <typename DataBoxType, typename... ComputeTags>
154 : void ObservationBox<tmpl::list<ComputeTags...>, DataBoxType>::reset() {
155 : tmpl::for_each<
156 : tmpl::filter<tmpl::list<ComputeTags...>, db::is_compute_tag<tmpl::_1>>>(
157 : [this](auto tag) {
158 : static_cast<db::detail::Item<tmpl::type_from<decltype(tag)>>&>(*this)
159 : .reset();
160 : });
161 : }
162 :
163 : template <typename DataBoxType, typename... ComputeTags>
164 : template <typename ComputeTag, typename... ArgumentTags>
165 : void ObservationBox<tmpl::list<ComputeTags...>, DataBoxType>::
166 : evaluate_compute_item(tmpl::list<ArgumentTags...> /*meta*/) const {
167 : get_item<ComputeTag>().evaluate(get<ArgumentTags>()...);
168 : }
169 :
170 : template <typename DataBoxType, typename... ComputeTags>
171 : template <typename ReferenceTag, typename... ArgumentTags>
172 : const auto&
173 : ObservationBox<tmpl::list<ComputeTags...>, DataBoxType>::get_reference_item(
174 : tmpl::list<ArgumentTags...> /*meta*/) const {
175 : return ReferenceTag::get(get<ArgumentTags>()...);
176 : }
177 :
178 : template <typename ComputeTagsList, typename DataBoxType>
179 0 : auto make_observation_box(const gsl::not_null<DataBoxType*> databox) {
180 : return ObservationBox<db::detail::expand_subitems<ComputeTagsList>,
181 : DataBoxType>{databox};
182 : }
183 :
184 : namespace observation_box_detail {
185 : template <typename DataBoxType, typename ComputeTagsList, typename... Args,
186 : typename F, typename... ArgumentTags>
187 : auto apply(F&& f, tmpl::list<ArgumentTags...> /*meta*/,
188 : const ObservationBox<ComputeTagsList, DataBoxType>& observation_box,
189 : Args&&... args) {
190 : if constexpr (db::detail::is_apply_callable_v<
191 : F,
192 : std::decay_t<decltype(
193 : get<ArgumentTags>(observation_box))>...,
194 : Args...>) {
195 : return std::decay_t<F>::apply(get<ArgumentTags>(observation_box)...,
196 : std::forward<Args>(args)...);
197 : } else if constexpr (::tt::is_callable_v<
198 : F,
199 : std::decay_t<decltype(get<ArgumentTags>(observation_box))>...,
200 : Args...>) {
201 : return std::forward<F>(f)(get<ArgumentTags>(observation_box)...,
202 : std::forward<Args>(args)...);
203 : } else {
204 : db::detail::error_function_not_callable<
205 : F, std::decay_t<decltype(get<ArgumentTags>(observation_box))>...,
206 : Args...>();
207 : }
208 : }
209 :
210 : template <typename DataBoxType, typename ComputeTagsList, typename... Args,
211 : typename F, typename... ReturnTags, typename... ArgumentTags>
212 : decltype(auto) mutate_apply(
213 : F&& f, tmpl::list<ReturnTags...> /*meta*/,
214 : tmpl::list<ArgumentTags...> /*meta*/,
215 : const gsl::not_null<ObservationBox<ComputeTagsList, DataBoxType>*>
216 : observation_box,
217 : Args&&... args) {
218 : const CleanupRoutine reset_items = [&]() {
219 : if constexpr (sizeof...(ReturnTags) != 0) {
220 : // Not ideal, but doing a more granular reset is not worth the
221 : // trouble.
222 : observation_box->reset();
223 : }
224 : };
225 : return db::mutate_apply<tmpl::list<ReturnTags...>, tmpl::list<>>(
226 : f, make_not_null(&observation_box->databox()),
227 : get<ArgumentTags>(*observation_box)..., std::forward<Args>(args)...);
228 : }
229 : } // namespace observation_box_detail
230 :
231 : /*!
232 : * \ingroup DataStructuresGroup
233 : * \brief Apply the function object `f` using its nested `argument_tags` list of
234 : * tags.
235 : */
236 : template <typename DataBoxType, typename ComputeTagsList, typename... Args,
237 : typename F>
238 1 : auto apply(F&& f,
239 : const ObservationBox<ComputeTagsList, DataBoxType>& observation_box,
240 : Args&&... args) {
241 : return observation_box_detail::apply(
242 : std::forward<F>(f), typename std::decay_t<F>::argument_tags{},
243 : observation_box, std::forward<Args>(args)...);
244 : }
245 :
246 : /*!
247 : * \ingroup DataStructuresGroup
248 : * \brief Apply the function object `f` using its nested `return_tags`
249 : * and `argument_tags` list of tags. Modifications are made to the
250 : * underlying DataBox.
251 : */
252 : /// @{
253 : template <typename DataBoxType, typename ComputeTagsList, typename... Args,
254 : typename F>
255 1 : auto mutate_apply(
256 : F&& f,
257 : const gsl::not_null<ObservationBox<ComputeTagsList, DataBoxType>*>
258 : observation_box,
259 : Args&&... args) {
260 : return observation_box_detail::mutate_apply(
261 : std::forward<F>(f), typename std::decay_t<F>::return_tags{},
262 : typename std::decay_t<F>::argument_tags{}, observation_box,
263 : std::forward<Args>(args)...);
264 : }
265 :
266 : template <typename ReturnTags, typename ArgumentTags, typename DataBoxType,
267 : typename ComputeTagsList, typename... Args, typename F>
268 1 : auto mutate_apply(
269 : F&& f,
270 : const gsl::not_null<ObservationBox<ComputeTagsList, DataBoxType>*>
271 : observation_box,
272 : Args&&... args) {
273 : return observation_box_detail::mutate_apply(std::forward<F>(f), ReturnTags{},
274 : ArgumentTags{}, observation_box,
275 : std::forward<Args>(args)...);
276 : }
277 : /// @}
|