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 : /// Volume tensor data to be written to disk. 97 1 : struct TensorData : db::SimpleTag { 98 0 : using type = std::unordered_map< 99 : observers::ObservationId, 100 : std::unordered_map<Parallel::ArrayComponentId, ElementVolumeData>>; 101 : }; 102 : 103 : /// Volume tensor data to be written to disk from the Interpolator 104 1 : struct InterpolatorTensorData : db::SimpleTag { 105 0 : using type = 106 : std::unordered_map<observers::ObservationId, 107 : std::unordered_map<Parallel::ArrayComponentId, 108 : std::vector<ElementVolumeData>>>; 109 : }; 110 : 111 : /// Map of serialized FunctionOfTime data for volume data to be written. 112 1 : struct SerializedFunctionsOfTime : db::SimpleTag { 113 0 : using type = 114 : std::unordered_map<ObservationId, std::optional<std::vector<char>>>; 115 : }; 116 : 117 : /// Map of ObservationIds that have dependencies for volume data to be written 118 : /// and whether or not the dependency has the volume data being written or 119 : /// discarded. 120 1 : struct Dependencies : db::SimpleTag { 121 0 : using type = std::unordered_map<ObservationId, std::pair<std::string, bool>>; 122 : }; 123 : 124 : /// \cond 125 : template <class... ReductionDatums> 126 : struct ReductionDataNames; 127 : /// \endcond 128 : 129 : /// Reduction data to be written to disk. 130 : /// 131 : /// If we have `M` `ArrayComponentIds` registered with a given `Observer` 132 : /// for a given `ObservationId`, then we expect the `Observer` to receive 133 : /// `M` contributions with the given `ObservationId`. We combine the reduction 134 : /// data as we receive it, so we only need one copy for each `ObservationId`. 135 : template <class... ReductionDatums> 136 1 : struct ReductionData : db::SimpleTag { 137 0 : using type = std::unordered_map<observers::ObservationId, 138 : Parallel::ReductionData<ReductionDatums...>>; 139 0 : using names_tag = ReductionDataNames<ReductionDatums...>; 140 : }; 141 : 142 : /// Names of the reduction data to be written to disk. 143 : template <class... ReductionDatums> 144 1 : struct ReductionDataNames : db::SimpleTag { 145 0 : using type = 146 : std::unordered_map<observers::ObservationId, std::vector<std::string>>; 147 0 : using data_tag = ReductionData<ReductionDatums...>; 148 : }; 149 : 150 : /// Node lock used when needing to read/write to H5 files on disk. 151 : /// 152 : /// The reason for only having one lock for all files is that we currently don't 153 : /// require a thread-safe HDF5 installation. In the future we will need to 154 : /// experiment with different HDF5 configurations. 155 1 : struct H5FileLock : db::SimpleTag { 156 0 : using type = Parallel::NodeLock; 157 : }; 158 : 159 : /*! 160 : * \brief A string identifying observations related to the `Tag`. 161 : * 162 : * For example, the `Tag` could refer to the block in the computational domain 163 : * and the `ObservationKey` holds the name of the block, so reduction 164 : * observations can be registered per-block. A value of `std::nullopt` means 165 : * that the element doesn't participate in observations related to the `Tag`. 166 : * 167 : * This tag only provides a way to store and share the observation key related 168 : * to the `Tag`. How it is used to construct composite observation keys or 169 : * subfile paths is up to the code that performs the observations. 170 : */ 171 : template <typename Tag> 172 1 : struct ObservationKey : db::SimpleTag { 173 0 : using type = std::optional<std::string>; 174 0 : static std::string name() { 175 : return "ObservationKey(" + pretty_type::name<Tag>() + ")"; 176 : } 177 : }; 178 : 179 : } // namespace Tags 180 : 181 : /// \ingroup ObserversGroup 182 : /// Option tags related to recording data 183 1 : namespace OptionTags { 184 : /// Groups option tags related to recording data, e.g. file names. 185 1 : struct Group { 186 0 : static std::string name() { return "Observers"; } 187 0 : static constexpr Options::String help = {"Options for recording data"}; 188 : }; 189 : 190 : /// The name of the H5 file on disk to which all volume data is written. 191 1 : struct VolumeFileName { 192 0 : using type = std::string; 193 0 : static constexpr Options::String help = { 194 : "Name of the volume data file without extension"}; 195 0 : using group = Group; 196 : }; 197 : 198 : /// The name of the H5 file on disk to which all reduction data is written. 199 1 : struct ReductionFileName { 200 0 : using type = std::string; 201 0 : static constexpr Options::String help = { 202 : "Name of the reduction data file without extension"}; 203 0 : using group = Group; 204 : }; 205 : 206 : /// The name of the H5 file on disk to which all surface data is written. 207 1 : struct SurfaceFileName { 208 0 : using type = std::string; 209 0 : static constexpr Options::String help = { 210 : "Name of the surface data file without extension"}; 211 0 : using group = Group; 212 : }; 213 : } // namespace OptionTags 214 : 215 : namespace Tags { 216 : /// \brief The name of the HDF5 file on disk into which volume data is 217 : /// written. 218 : /// 219 : /// By volume data we mean any data that is not written once across all nodes. 220 : /// For example, data on a 2d surface written from a 2d simulation is 221 : /// considered volume data, while an integral over the entire (or a subset of 222 : /// the) domain is considered reduction data. Data for a 2d surface in 223 : /// a 3d simulation, such as a horizon, is considered surface data and 224 : /// is written to a file specified by SurfaceFileName. 225 1 : struct VolumeFileName : db::SimpleTag { 226 0 : using type = std::string; 227 0 : using option_tags = tmpl::list<::observers::OptionTags::VolumeFileName>; 228 : 229 0 : static constexpr bool pass_metavariables = false; 230 0 : static std::string create_from_options(const std::string& volume_file_name) { 231 : return volume_file_name; 232 : } 233 : }; 234 : 235 : /// \brief The name of the HDF5 file on disk into which reduction data is 236 : /// written. 237 : /// 238 : /// By reduction data we mean any data that is written once across all nodes. 239 : /// For example, an integral over the entire (or a subset of the) domain 240 : /// is considered reduction data, while data on a 3d surface written from a 3d 241 : /// simulation is considered volume data. Data for a 2d surface in 242 : /// a 3d simulation, such as a horizon, is considered surface data and 243 : /// is written to a file specified by SurfaceFileName. 244 1 : struct ReductionFileName : db::SimpleTag { 245 0 : using type = std::string; 246 0 : using option_tags = tmpl::list<::observers::OptionTags::ReductionFileName>; 247 : 248 0 : static constexpr bool pass_metavariables = false; 249 0 : static std::string create_from_options( 250 : const std::string& reduction_file_name) { 251 : return reduction_file_name; 252 : } 253 : }; 254 : 255 : /// \brief The name of the HDF5 file on disk into which surface data is 256 : /// written. 257 : /// 258 : /// By surface data we mean data for a 2d surface in 259 : /// a 3d simulation, such as a horizon. Surface data is written once across 260 : /// all nodes. For example, data on a 2d surface written from a 2d simulation is 261 : /// considered volume data, while an integral over the entire (or a subset of 262 : /// the) domain is considered reduction data. 263 1 : struct SurfaceFileName : db::SimpleTag { 264 0 : using type = std::string; 265 0 : using option_tags = tmpl::list<::observers::OptionTags::SurfaceFileName>; 266 : 267 0 : static constexpr bool pass_metavariables = false; 268 0 : static std::string create_from_options(const std::string& surface_file_name) { 269 : return surface_file_name; 270 : } 271 : }; 272 : } // namespace Tags 273 : } // namespace observers