ReadVolumeData.hpp
1 // Distributed under the MIT License.
2 // See LICENSE.txt for details.
3 
4 #pragma once
5 
6 #include <cstddef>
7 #include <tuple>
8 
10 #include "DataStructures/DataVector.hpp"
13 #include "IO/H5/AccessType.hpp"
14 #include "IO/H5/File.hpp"
15 #include "IO/H5/VolumeData.hpp"
16 #include "IO/Importers/Tags.hpp"
17 #include "IO/Observer/ArrayComponentId.hpp"
18 #include "Parallel/ArrayIndex.hpp"
19 #include "Parallel/GlobalCache.hpp"
20 #include "Parallel/Info.hpp"
21 #include "Parallel/Invoke.hpp"
22 #include "Utilities/Gsl.hpp"
23 #include "Utilities/Literals.hpp"
24 #include "Utilities/Requires.hpp"
25 #include "Utilities/TMPL.hpp"
26 #include "Utilities/TaggedTuple.hpp"
27 
28 /// \cond
29 namespace importers {
30 template <typename Metavariables>
31 struct ElementDataReader;
32 namespace Actions {
33 template <typename ImporterOptionsGroup, typename FieldTagsList,
34  typename ReceiveComponent>
35 struct ReadAllVolumeDataAndDistribute;
36 } // namespace Actions
37 } // namespace importers
38 /// \endcond
39 
40 namespace importers::Actions {
41 
42 /*!
43  * \brief Read a volume data file and distribute the data to all registered
44  * elements.
45  *
46  * Invoke this action on the elements of an array parallel component to dispatch
47  * reading the volume data file specified by the options in
48  * `ImporterOptionsGroup`. The tensors in `FieldTagsList` will be loaded from
49  * the file and distributed to all elements that have previously registered. Use
50  * `importers::Actions::RegisterWithElementDataReader` to register the elements
51  * of the array parallel component in a previous phase.
52  *
53  * Note that the volume data file will only be read once per node, triggered by
54  * the first element that invokes this action. All subsequent invocations of
55  * this action on the node will do nothing. See
56  * `importers::Actions::ReadAllVolumeDataAndDistribute` for details.
57  *
58  * The data is distributed to the elements using `Parallel::receive_data`. The
59  * elements can monitor `importers::Tags::VolumeData` in their inbox to wait for
60  * the data and process it once it's available. We provide the action
61  * `importers::Actions::ReceiveVolumeData` that waits for the data and moves it
62  * directly into the DataBox. You can also implement a specialized action that
63  * might verify and post-process the data before populating the DataBox.
64  *
65  * \see Dev guide on \ref dev_guide_importing
66  */
67 template <typename ImporterOptionsGroup, typename FieldTagsList>
69  using const_global_cache_tags =
70  tmpl::list<Tags::FileName<ImporterOptionsGroup>,
73 
74  template <typename DbTagsList, typename... InboxTags, typename Metavariables,
75  typename ArrayIndex, typename ActionList,
76  typename ParallelComponent>
77  static std::tuple<db::DataBox<DbTagsList>&&> apply(
79  const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
81  const ArrayIndex& /*array_index*/, const ActionList /*meta*/,
82  const ParallelComponent* const /*meta*/) noexcept {
83  // Not using `ckLocalBranch` here to make sure the simple action invocation
84  // is asynchronous.
85  auto& reader_component = Parallel::get_parallel_component<
88  ImporterOptionsGroup, FieldTagsList, ParallelComponent>>(
89  reader_component);
90  return {std::move(box)};
91  }
92 };
93 
94 /*!
95  * \brief Read a volume data file and distribute the data to all registered
96  * elements.
97  *
98  * This action can be invoked on the `importers::ElementDataReader` component
99  * once all elements have been registered with it. It opens the data file, reads
100  * the data for each registered element and uses `Parallel::receive_data` to
101  * distribute the data to the elements. The elements can monitor
102  * `importers::Tags::VolumeData` in their inbox to wait for the data and process
103  * it once it's available. You can use `importers::Actions::ReceiveVolumeData`
104  * to wait for the data and move it directly into the DataBox, or implement a
105  * specialized action that might verify and post-process the data.
106  *
107  * Note that instead of invoking this action directly on the
108  * `importers::ElementDataReader` component you can invoke the iterable action
109  * `importers::Actions::ReadVolumeData` on the elements of an array parallel
110  * component.
111  *
112  * - The `ImporterOptionsGroup` parameter specifies the \ref OptionGroupsGroup
113  * "options group" in the input file that provides the following run-time
114  * options:
115  * - `importers::OptionTags::FileName`
116  * - `importers::OptionTags::Subgroup`
117  * - `importers::OptionTags::ObservationValue`
118  * - The `FieldTagsList` parameter specifies a typelist of tensor tags that
119  * are read from the file and provided to each element. It is assumed that the
120  * tensor data is stored in datasets named `db::tag_name<Tag>() + suffix`, where
121  * the `suffix` is empty for scalars or `"_"` followed by the
122  * `Tensor::component_name` for each independent tensor component.
123  * - `Parallel::receive_data` is invoked on each registered element of the
124  * `ReceiveComponent` to populate `importers::Tags::VolumeData` in the element's
125  * inbox with a `tuples::tagged_tuple_from_typelist<FieldTagsList>` containing
126  * the tensor data for that element. The `ReceiveComponent` must the the same
127  * that was encoded into the `observers::ArrayComponentId` used to register the
128  * elements.
129  *
130  * \see Dev guide on \ref dev_guide_importing
131  */
132 template <typename ImporterOptionsGroup, typename FieldTagsList,
133  typename ReceiveComponent>
135  template <
136  typename ParallelComponent, typename DataBox, typename Metavariables,
137  typename ArrayIndex,
139  db::tag_is_retrievable_v<Tags::ElementDataAlreadyRead,
140  DataBox>> = nullptr>
141  static void apply(DataBox& box, Parallel::GlobalCache<Metavariables>& cache,
142  const ArrayIndex& /*array_index*/) noexcept {
143  // Only read and distribute the volume data once
144  // This action will be invoked by `importers::Actions::ReadVolumeData` from
145  // every element on the node, but only the first invocation reads the file
146  // and distributes the data to all elements. Subsequent invocations do
147  // nothing. We use the `ImporterOptionsGroup` that specifies the data file
148  // to read in as the identifier for whether or not we have already read the
149  // requested data. Doing this at runtime avoids having to collect all
150  // data files that will be read in at compile-time to initialize a flag in
151  // the DataBox for each of them.
152  const auto& has_read_volume_data =
153  db::get<Tags::ElementDataAlreadyRead>(box);
154  const auto volume_data_id = pretty_type::get_name<ImporterOptionsGroup>();
155  if (has_read_volume_data.find(volume_data_id) !=
156  has_read_volume_data.end()) {
157  return;
158  }
159  db::mutate<Tags::ElementDataAlreadyRead>(
160  make_not_null(&box),
161  [&volume_data_id](const gsl::not_null<std::unordered_set<std::string>*>
162  local_has_read_volume_data) noexcept {
163  local_has_read_volume_data->insert(std::move(volume_data_id));
164  });
165 
166  // Open the volume data file
169  constexpr size_t version_number = 0;
170  const auto& volume_file = h5file.get<h5::VolumeData>(
171  "/" + Parallel::get<Tags::Subgroup<ImporterOptionsGroup>>(cache),
172  version_number);
173  const auto observation_id = volume_file.find_observation_id(
175  // Read the tensor data for all elements at once, since that's how it's
176  // stored in the file
177  tuples::tagged_tuple_from_typelist<FieldTagsList> all_tensor_data{};
178  tmpl::for_each<FieldTagsList>([&all_tensor_data, &volume_file,
179  &observation_id](auto field_tag_v) noexcept {
180  using field_tag = tmpl::type_from<decltype(field_tag_v)>;
181  auto& tensor_data = get<field_tag>(all_tensor_data);
182  for (size_t i = 0; i < tensor_data.size(); i++) {
183  tensor_data[i] = volume_file.get_tensor_component(
184  observation_id,
185  db::tag_name<field_tag>() +
186  tensor_data.component_suffix(tensor_data.get_tensor_index(i)));
187  }
188  });
189  // Retrieve the information needed to reconstruct which element the data
190  // belongs to
191  const auto all_grid_names = volume_file.get_grid_names(observation_id);
192  const auto all_extents = volume_file.get_extents(observation_id);
193  // Distribute the tensor data to the registered elements
194  for (auto& element_and_name : get<Tags::RegisteredElements>(box)) {
195  const CkArrayIndex& raw_element_index =
196  element_and_name.first.array_index();
197  // Check if the parallel component of the registered element matches the
198  // callback, because it's possible that elements from other components
199  // with the same index are also registered.
200  // Since the way the component is encoded in `ArrayComponentId` is
201  // private to that class, we construct one and compare.
202  if (element_and_name.first !=
205  raw_element_index)) {
206  continue;
207  }
208  // Find the data offset that corresponds to this element
209  const auto element_data_offset_and_length =
210  h5::offset_and_length_for_grid(element_and_name.second,
211  all_grid_names, all_extents);
212  // Extract this element's data from the read-in dataset
213  tuples::tagged_tuple_from_typelist<FieldTagsList> element_data{};
214  tmpl::for_each<FieldTagsList>([&element_data,
215  &element_data_offset_and_length,
216  &all_tensor_data](
217  auto field_tag_v) noexcept {
218  using field_tag = tmpl::type_from<decltype(field_tag_v)>;
219  auto& element_tensor_data = get<field_tag>(element_data);
220  // Iterate independent components of the tensor
221  for (size_t i = 0; i < element_tensor_data.size(); i++) {
222  const DataVector& data_tensor_component =
223  get<field_tag>(all_tensor_data)[i];
224  DataVector element_tensor_component{
225  element_data_offset_and_length.second};
226  // Retrieve data from slice of the contigious dataset
227  for (size_t j = 0; j < element_tensor_component.size(); j++) {
228  element_tensor_component[j] =
229  data_tensor_component[element_data_offset_and_length.first + j];
230  }
231  element_tensor_data[i] = element_tensor_component;
232  }
233  });
234  // Pass the data to the element
235  const auto element_index =
237  raw_element_index)
238  .get_index();
241  Parallel::get_parallel_component<ReceiveComponent>(
242  cache)[element_index],
243  // Using `0` for the temporal ID since we only read the volume data
244  // once, so there's no need to keep track of the temporal ID.
245  0_st, std::move(element_data));
246  }
247  }
248 };
249 
250 } // namespace importers::Actions
AccessType.hpp
importers::Tags::Subgroup
The subgroup within the file to read data from.
Definition: Tags.hpp:134
Parallel::GlobalCache
Definition: ElementReceiveInterpPoints.hpp:16
std::unordered_set< std::string >
Literals.hpp
GlobalCache.hpp
Parallel::get_parallel_component
auto get_parallel_component(GlobalCache< Metavariables > &cache) noexcept -> Parallel::proxy_from_parallel_component< GlobalCache_detail::get_component_if_mocked< typename Metavariables::component_list, ParallelComponentTag >> &
Access the Charm++ proxy associated with a ParallelComponent.
Definition: GlobalCache.hpp:223
importers
Items related to loading data from files.
Definition: ReadSpecThirdOrderPiecewisePolynomial.hpp:32
tuple
importers::Tags::VolumeData
Inbox tag that carries the data read from a volume data file.
Definition: Tags.hpp:189
Info.hpp
File.hpp
ElementId.hpp
h5::H5File< h5::AccessType::ReadOnly >
std::add_pointer_t
DataBox.hpp
importers::ElementDataReader
A nodegroup parallel component that reads in a volume data file and distributes its data to elements ...
Definition: ElementDataReader.hpp:37
h5::offset_and_length_for_grid
std::pair< size_t, size_t > offset_and_length_for_grid(const std::string &grid_name, const std::vector< std::string > &all_grid_names, const std::vector< std::vector< size_t >> &all_extents) noexcept
Find the interval within the contiguous dataset stored in h5::VolumeData that holds data for a partic...
cstddef
importers::Tags::ObservationValue
The observation value at which to read data from the file.
Definition: Tags.hpp:151
tuples::TaggedTuple
An associative container that is indexed by structs.
Definition: TaggedTuple.hpp:271
DataVector
Stores a collection of function values.
Definition: DataVector.hpp:42
Parallel::receive_data
void receive_data(Proxy &&proxy, typename ReceiveTag::temporal_id temporal_id, ReceiveDataType &&receive_data, const bool enable_if_disabled=false) noexcept
Send the data args... to the algorithm running on proxy, and tag the message with the identifier temp...
Definition: Invoke.hpp:47
Gsl.hpp
Tensor.hpp
Requires.hpp
observers::ArrayComponentId
An ID type that identifies both the parallel component and the index in the parallel component.
Definition: ArrayComponentId.hpp:27
Parallel::simple_action
void simple_action(Proxy &&proxy) noexcept
Invoke a simple action on proxy
Definition: Invoke.hpp:82
make_not_null
gsl::not_null< T * > make_not_null(T *ptr) noexcept
Construct a not_null from a pointer. Often this will be done as an implicit conversion,...
Definition: Gsl.hpp:880
Parallel::ArrayIndex
The array index used for indexing Chare Arrays, mostly an implementation detail.
Definition: ArrayIndex.hpp:27
Requires
typename Requires_detail::requires_impl< B >::template_error_type_failed_to_meet_requirements_on_template_parameters Requires
Express requirements on the template parameters of a function or class, replaces std::enable_if_t
Definition: Requires.hpp:67
importers::Tags::FileName
The file to read data from.
Definition: Tags.hpp:115
importers::Actions::ReadAllVolumeDataAndDistribute
Read a volume data file and distribute the data to all registered elements.
Definition: ReadVolumeData.hpp:134
importers::Actions::ReadVolumeData
Read a volume data file and distribute the data to all registered elements.
Definition: ReadVolumeData.hpp:68
db::DataBox
Definition: InterpolationTargetWedgeSectionTorus.hpp:24
Parallel::get
auto get(const GlobalCache< Metavariables > &cache) noexcept -> const GlobalCache_detail::type_for_get< GlobalCacheTag, Metavariables > &
Access data in the cache.
Definition: GlobalCache.hpp:254
TMPL.hpp
h5::VolumeData
A volume data subfile written inside an H5 file.
Definition: VolumeData.hpp:68
gsl::not_null
Require a pointer to not be a nullptr
Definition: Gsl.hpp:183
importers::Tags::ElementDataAlreadyRead
Indicates which volume data files have already been read.
Definition: Tags.hpp:178