Line data Source code
1 0 : // Distributed under the MIT License. 2 : // See LICENSE.txt for details. 3 : 4 : #pragma once 5 : 6 : #include <atomic> 7 : #include <converse.h> 8 : #include <cstddef> 9 : #include <memory> 10 : #include <set> 11 : #include <string> 12 : #include <unordered_map> 13 : #include <unordered_set> 14 : #include <vector> 15 : 16 : #include "DataStructures/DataBox/Tag.hpp" 17 : #include "DataStructures/DataVector.hpp" 18 : #include "IO/H5/TensorData.hpp" 19 : #include "IO/Observer/ObservationId.hpp" 20 : #include "Options/String.hpp" 21 : #include "Parallel/ArrayComponentId.hpp" 22 : #include "Parallel/NodeLock.hpp" 23 : #include "Parallel/Reduction.hpp" 24 : #include "Utilities/PrettyType.hpp" 25 : 26 : namespace observers { 27 : /// \ingroup ObserversGroup 28 : /// %Tags used on the observer parallel component 29 1 : namespace Tags { 30 : /// \brief The set of `ArrayComponentId`s that will contribute to each 31 : /// `ObservationId` for reduction. 32 : /// 33 : /// This is set during registration is only read from later on in the 34 : /// simulation, except for possibly during migration of an `ArrayComponentId` 35 : /// component. 36 1 : struct ExpectedContributorsForObservations : db::SimpleTag { 37 0 : using type = 38 : std::unordered_map<ObservationKey, 39 : std::unordered_set<Parallel::ArrayComponentId>>; 40 : }; 41 : 42 : /// \brief The set of `ArrayComponentId` that have contributed to each 43 : /// `ObservationId` for reductions 44 : /// 45 : /// The tag is used both on the `Observer` and on the `ObserverWriter` 46 : /// components since all we need to do is keep track of array component IDs in 47 : /// both cases. 48 1 : struct ContributorsOfReductionData : db::SimpleTag { 49 0 : using type = 50 : std::unordered_map<ObservationId, 51 : std::unordered_set<Parallel::ArrayComponentId>>; 52 : }; 53 : 54 : /// \brief The set of nodes that have contributed to each `ObservationId` for 55 : /// writing reduction data 56 : /// 57 : /// This is used on node 0 (or whichever node has been designated as the one to 58 : /// write the reduction files). The `unordered_set` is the node IDs that have 59 : /// contributed so far. 60 1 : struct NodesThatContributedReductions : db::SimpleTag { 61 0 : using type = std::unordered_map<ObservationId, std::unordered_set<size_t>>; 62 : }; 63 : 64 : /// \brief The set of nodes that are registered with each 65 : /// `ObservationIdRegistrationKey` for writing reduction data 66 : /// 67 : /// The set contains all the nodes that have been registered. 68 : /// 69 : /// We need to keep track of this separately from the local reductions on the 70 : /// node that are contributing so we need a separate tag. Since nodes are easily 71 : /// indexed by an unsigned integer, we use `size_t` to keep track of them. 72 1 : struct NodesExpectedToContributeReductions : db::SimpleTag { 73 0 : using type = std::unordered_map<ObservationKey, std::set<size_t>>; 74 : }; 75 : 76 : /// \brief Lock used when contributing reduction data. 77 : /// 78 : /// A separate lock from the node lock of the nodegroup is used in order to 79 : /// allow other cores to contribute volume data, write to disk, etc. 80 1 : struct ReductionDataLock : db::SimpleTag { 81 0 : using type = Parallel::NodeLock; 82 : }; 83 : 84 : /// \brief The set of `ArrayComponentId` that have contributed to each 85 : /// `ObservationId` for volume observation 86 : /// 87 : /// The tag is used both on the `Observer` and on the `ObserverWriter` 88 : /// components since all we need to do is keep track of array component IDs in 89 : /// both cases. 90 1 : struct ContributorsOfTensorData : db::SimpleTag { 91 0 : using type = 92 : std::unordered_map<ObservationId, 93 : std::unordered_set<Parallel::ArrayComponentId>>; 94 : }; 95 : 96 : /// \brief Lock used when contributing volume data. 97 : /// 98 : /// A separate lock from the node lock of the nodegroup is used in order to 99 : /// allow other cores to contribute reduction data, write to disk, etc. 100 1 : struct VolumeDataLock : db::SimpleTag { 101 0 : using type = Parallel::NodeLock; 102 : }; 103 : 104 : /// Volume tensor data to be written to disk. 105 1 : struct TensorData : db::SimpleTag { 106 0 : using type = std::unordered_map< 107 : observers::ObservationId, 108 : std::unordered_map<Parallel::ArrayComponentId, ElementVolumeData>>; 109 : }; 110 : 111 : /// Volume tensor data to be written to disk from the Interpolator 112 1 : struct InterpolatorTensorData : db::SimpleTag { 113 0 : using type = 114 : std::unordered_map<observers::ObservationId, 115 : std::unordered_map<Parallel::ArrayComponentId, 116 : std::vector<ElementVolumeData>>>; 117 : }; 118 : 119 : /// \cond 120 : template <class... ReductionDatums> 121 : struct ReductionDataNames; 122 : /// \endcond 123 : 124 : /// Reduction data to be written to disk. 125 : /// 126 : /// If we have `M` `ArrayComponentIds` registered with a given `Observer` 127 : /// for a given `ObservationId`, then we expect the `Observer` to receive 128 : /// `M` contributions with the given `ObservationId`. We combine the reduction 129 : /// data as we receive it, so we only need one copy for each `ObservationId`. 130 : template <class... ReductionDatums> 131 1 : struct ReductionData : db::SimpleTag { 132 0 : using type = std::unordered_map<observers::ObservationId, 133 : Parallel::ReductionData<ReductionDatums...>>; 134 0 : using names_tag = ReductionDataNames<ReductionDatums...>; 135 : }; 136 : 137 : /// Names of the reduction data to be written to disk. 138 : template <class... ReductionDatums> 139 1 : struct ReductionDataNames : db::SimpleTag { 140 0 : using type = 141 : std::unordered_map<observers::ObservationId, std::vector<std::string>>; 142 0 : using data_tag = ReductionData<ReductionDatums...>; 143 : }; 144 : 145 : /// Node lock used when needing to read/write to H5 files on disk. 146 : /// 147 : /// The reason for only having one lock for all files is that we currently don't 148 : /// require a thread-safe HDF5 installation. In the future we will need to 149 : /// experiment with different HDF5 configurations. 150 1 : struct H5FileLock : db::SimpleTag { 151 0 : using type = Parallel::NodeLock; 152 : }; 153 : 154 : /*! 155 : * \brief A string identifying observations related to the `Tag`. 156 : * 157 : * For example, the `Tag` could refer to the block in the computational domain 158 : * and the `ObservationKey` holds the name of the block, so reduction 159 : * observations can be registered per-block. A value of `std::nullopt` means 160 : * that the element doesn't participate in observations related to the `Tag`. 161 : * 162 : * This tag only provides a way to store and share the observation key related 163 : * to the `Tag`. How it is used to construct composite observation keys or 164 : * subfile paths is up to the code that performs the observations. 165 : */ 166 : template <typename Tag> 167 1 : struct ObservationKey : db::SimpleTag { 168 0 : using type = std::optional<std::string>; 169 0 : static std::string name() { 170 : return "ObservationKey(" + pretty_type::name<Tag>() + ")"; 171 : } 172 : }; 173 : 174 : } // namespace Tags 175 : 176 : /// \ingroup ObserversGroup 177 : /// Option tags related to recording data 178 1 : namespace OptionTags { 179 : /// Groups option tags related to recording data, e.g. file names. 180 1 : struct Group { 181 0 : static std::string name() { return "Observers"; } 182 0 : static constexpr Options::String help = {"Options for recording data"}; 183 : }; 184 : 185 : /// The name of the H5 file on disk to which all volume data is written. 186 1 : struct VolumeFileName { 187 0 : using type = std::string; 188 0 : static constexpr Options::String help = { 189 : "Name of the volume data file without extension"}; 190 0 : using group = Group; 191 : }; 192 : 193 : /// The name of the H5 file on disk to which all reduction data is written. 194 1 : struct ReductionFileName { 195 0 : using type = std::string; 196 0 : static constexpr Options::String help = { 197 : "Name of the reduction data file without extension"}; 198 0 : using group = Group; 199 : }; 200 : 201 : /// The name of the H5 file on disk to which all surface data is written. 202 1 : struct SurfaceFileName { 203 0 : using type = std::string; 204 0 : static constexpr Options::String help = { 205 : "Name of the surface data file without extension"}; 206 0 : using group = Group; 207 : }; 208 : } // namespace OptionTags 209 : 210 : namespace Tags { 211 : /// \brief The name of the HDF5 file on disk into which volume data is 212 : /// written. 213 : /// 214 : /// By volume data we mean any data that is not written once across all nodes. 215 : /// For example, data on a 2d surface written from a 2d simulation is 216 : /// considered volume data, while an integral over the entire (or a subset of 217 : /// the) domain is considered reduction data. Data for a 2d surface in 218 : /// a 3d simulation, such as a horizon, is considered surface data and 219 : /// is written to a file specified by SurfaceFileName. 220 1 : struct VolumeFileName : db::SimpleTag { 221 0 : using type = std::string; 222 0 : using option_tags = tmpl::list<::observers::OptionTags::VolumeFileName>; 223 : 224 0 : static constexpr bool pass_metavariables = false; 225 0 : static std::string create_from_options(const std::string& volume_file_name) { 226 : return volume_file_name; 227 : } 228 : }; 229 : 230 : /// \brief The name of the HDF5 file on disk into which reduction data is 231 : /// written. 232 : /// 233 : /// By reduction data we mean any data that is written once across all nodes. 234 : /// For example, an integral over the entire (or a subset of the) domain 235 : /// is considered reduction data, while data on a 3d surface written from a 3d 236 : /// simulation is considered volume data. Data for a 2d surface in 237 : /// a 3d simulation, such as a horizon, is considered surface data and 238 : /// is written to a file specified by SurfaceFileName. 239 1 : struct ReductionFileName : db::SimpleTag { 240 0 : using type = std::string; 241 0 : using option_tags = tmpl::list<::observers::OptionTags::ReductionFileName>; 242 : 243 0 : static constexpr bool pass_metavariables = false; 244 0 : static std::string create_from_options( 245 : const std::string& reduction_file_name) { 246 : return reduction_file_name; 247 : } 248 : }; 249 : 250 : /// \brief The name of the HDF5 file on disk into which surface data is 251 : /// written. 252 : /// 253 : /// By surface data we mean data for a 2d surface in 254 : /// a 3d simulation, such as a horizon. Surface data is written once across 255 : /// all nodes. For example, data on a 2d surface written from a 2d simulation is 256 : /// considered volume data, while an integral over the entire (or a subset of 257 : /// the) domain is considered reduction data. 258 1 : struct SurfaceFileName : db::SimpleTag { 259 0 : using type = std::string; 260 0 : using option_tags = tmpl::list<::observers::OptionTags::SurfaceFileName>; 261 : 262 0 : static constexpr bool pass_metavariables = false; 263 0 : static std::string create_from_options(const std::string& surface_file_name) { 264 : return surface_file_name; 265 : } 266 : }; 267 : } // namespace Tags 268 : } // namespace observers