SpECTRE Documentation Coverage Report
Current view: top level - Parallel - GlobalCache.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 40.5 % 79 32
Test Date: 2026-10-22 17:29:42
Legend: Lines:     hit not hit

            Line data    Source code
       1            1 : // Distributed under the MIT License.
       2              : // See LICENSE.txt for details.
       3              : 
       4              : /// \file
       5              : /// Defines class template GlobalCache.
       6              : 
       7              : #pragma once
       8              : 
       9              : #include <charm++.h>
      10              : #include <memory>
      11              : #include <mutex>
      12              : #include <numeric>
      13              : #include <optional>
      14              : #include <pup.h>
      15              : #include <string>
      16              : #include <tuple>
      17              : #include <unordered_map>
      18              : #include <utility>
      19              : #include <vector>
      20              : 
      21              : #include "DataStructures/DataBox/Tag.hpp"
      22              : #include "DataStructures/DataBox/TagTraits.hpp"
      23              : #include "DataStructures/TaggedTuple.hpp"
      24              : #include "Parallel/ArrayComponentId.hpp"
      25              : #include "Parallel/Callback.hpp"
      26              : #include "Parallel/CharmRegistration.hpp"
      27              : #include "Parallel/Info.hpp"
      28              : #include "Parallel/Local.hpp"
      29              : #include "Parallel/ParallelComponentHelpers.hpp"
      30              : #include "Parallel/ResourceInfo.hpp"
      31              : #include "Parallel/Tags/ResourceInfo.hpp"
      32              : #include "Utilities/Algorithm.hpp"
      33              : #include "Utilities/ErrorHandling/Assert.hpp"
      34              : #include "Utilities/ErrorHandling/Error.hpp"
      35              : #include "Utilities/Gsl.hpp"
      36              : #include "Utilities/Numeric.hpp"
      37              : #include "Utilities/PrettyType.hpp"
      38              : #include "Utilities/Requires.hpp"
      39              : #include "Utilities/Serialization/PupStlCpp17.hpp"
      40              : #include "Utilities/Serialization/Serialize.hpp"
      41              : #include "Utilities/StdHelpers/RetrieveUniquePtr.hpp"
      42              : #include "Utilities/System/ParallelInfo.hpp"
      43              : #include "Utilities/TMPL.hpp"
      44              : #include "Utilities/TypeTraits/CreateGetTypeAliasOrDefault.hpp"
      45              : #include "Utilities/TypeTraits/IsA.hpp"
      46              : 
      47              : #include "Parallel/GlobalCache.decl.h"
      48              : #include "Parallel/Main.decl.h"
      49              : 
      50              : /// \cond
      51              : namespace Parallel {
      52              : template <typename Metavariables>
      53              : struct GlobalCache;
      54              : }  // namespace Parallel
      55              : namespace mem_monitor {
      56              : template <typename Metavariables>
      57              : struct MemoryMonitor;
      58              : template <typename ContributingComponent>
      59              : struct ContributeMemoryData;
      60              : }  // namespace mem_monitor
      61              : /// \endcond
      62              : 
      63              : namespace Parallel {
      64              : 
      65              : namespace GlobalCache_detail {
      66              : 
      67              : template <class GlobalCacheTag, class Metavariables>
      68              : using get_matching_tag = typename matching_tag_helper<
      69              :     GlobalCacheTag,
      70              :     tmpl::append<get_const_global_cache_tags<Metavariables>,
      71              :                  get_mutable_global_cache_tags<Metavariables>>>::type;
      72              : 
      73              : template <class GlobalCacheTag, class Metavariables>
      74              : using type_for_get = typename type_for_get_helper<
      75              :     typename get_matching_tag<GlobalCacheTag, Metavariables>::type>::type;
      76              : 
      77              : CREATE_GET_TYPE_ALIAS_OR_DEFAULT(component_being_mocked)
      78              : 
      79              : template <typename... Tags>
      80              : auto make_mutable_cache_tag_storage(tuples::TaggedTuple<Tags...>&& input) {
      81              :   return tuples::TaggedTuple<MutableCacheTag<Tags>...>(std::make_tuple(
      82              :       std::move(tuples::get<Tags>(input)),
      83              :       std::unordered_map<Parallel::ArrayComponentId,
      84              :                          std::vector<std::unique_ptr<Callback>>>{})...);
      85              : }
      86              : 
      87              : template <typename ParallelComponent, typename ComponentList>
      88              : auto get_component_if_mocked_impl() {
      89              :   if constexpr (tmpl::list_contains_v<ComponentList, ParallelComponent>) {
      90              :     return ParallelComponent{};
      91              :   } else {
      92              :     using mock_find = tmpl::find<
      93              :         ComponentList,
      94              :         std::is_same<get_component_being_mocked_or_default<tmpl::_1, void>,
      95              :                      tmpl::pin<ParallelComponent>>>;
      96              :     static_assert(
      97              :         tmpl::size<mock_find>::value > 0,
      98              :         "The requested parallel component (first template argument) is not a "
      99              :         "known parallel component (second template argument) or a component "
     100              :         "being mocked by one of those components.");
     101              :     return tmpl::front<mock_find>{};
     102              :   }
     103              : }
     104              : 
     105              : /// In order to be able to use a mock action testing framework we need to be
     106              : /// able to get the correct parallel component from the global cache even when
     107              : /// the correct component is a mock. We do this by having the mocked
     108              : /// components have a member type alias `component_being_mocked`, and having
     109              : /// `Parallel::get_component` check if the component to be retrieved is in the
     110              : /// `metavariables::component_list`. If it is not in the `component_list` then
     111              : /// we search for a mock component that is mocking the component we are trying
     112              : /// to retrieve.
     113              : template <typename ComponentList, typename ParallelComponent>
     114              : using get_component_if_mocked =
     115              :     decltype(get_component_if_mocked_impl<ParallelComponent, ComponentList>());
     116              : 
     117              : /// This class replaces the Charm++ base class of the GlobalCache in unit tests
     118              : /// that don't have a Charm++ main function.
     119              : struct MockGlobalCache {
     120              :   MockGlobalCache() = default;
     121              :   explicit MockGlobalCache(CkMigrateMessage* /*msg*/) {}
     122              :   virtual ~MockGlobalCache() = default;
     123              :   virtual void pup(PUP::er& /*p*/) {}
     124              : 
     125              :   static bool isIrreducible() { return true; }
     126              : 
     127              : #if defined(__GNUC__) && !defined(__clang__)
     128              : #pragma GCC diagnostic push
     129              : #pragma GCC diagnostic ignored "-Wsuggest-attribute=noreturn"
     130              : #elif defined(__clang__)
     131              : #pragma GCC diagnostic push
     132              : #pragma GCC diagnostic ignored "-Wmissing-noreturn"
     133              : #endif  // defined(__GNUC__) && !defined(__clang__)
     134              :   template <typename T>
     135              :   static void contribute(T&& /*unused*/) {
     136              :     ERROR("Not implemented.");
     137              :   }
     138              : #if defined(__GNUC__) || defined(__clang__)
     139              : #pragma GCC diagnostic pop
     140              : #endif
     141              : };
     142              : 
     143              : #ifdef SPECTRE_CHARM_HAS_MAIN
     144              : static constexpr bool mock_global_cache = false;
     145              : #else
     146              : static constexpr bool mock_global_cache = true;
     147              : #endif  // SPECTRE_CHARM_HAS_MAIN
     148              : 
     149              : }  // namespace GlobalCache_detail
     150              : 
     151              : /// \cond
     152              : template <typename ParallelComponentTag, typename Metavariables>
     153              : auto get_parallel_component(GlobalCache<Metavariables>& cache)
     154              :     -> Parallel::proxy_from_parallel_component<
     155              :         GlobalCache_detail::get_component_if_mocked<
     156              :             typename Metavariables::component_list, ParallelComponentTag>>&;
     157              : 
     158              : template <typename ParallelComponentTag, typename Metavariables>
     159              : auto get_parallel_component(const GlobalCache<Metavariables>& cache)
     160              :     -> const Parallel::proxy_from_parallel_component<
     161              :         GlobalCache_detail::get_component_if_mocked<
     162              :             typename Metavariables::component_list, ParallelComponentTag>>&;
     163              : /// \endcond
     164              : 
     165              : /*!
     166              :  * \ingroup ParallelGroup
     167              :  * \brief A Charm++ chare that caches global data once per Charm++ node.
     168              :  *
     169              :  * \details There are two types of global data that are stored; const data and
     170              :  * mutable data. Once the GlobalCache is created, const data cannot be edited
     171              :  * but mutable data can be edited using `Parallel::mutate`.
     172              :  *
     173              :  * The template parameter `Metavariables` must define the following type
     174              :  * aliases:
     175              :  *   - `component_list`   typelist of ParallelComponents
     176              :  *   - `const_global_cache_tags`   (possibly empty) typelist of tags of
     177              :  *     constant data
     178              :  *   - `mutable_global_cache_tags` (possibly empty) typelist of tags of
     179              :  *     non-constant data
     180              :  *
     181              :  * The tag lists for the const items added to the GlobalCache is created by
     182              :  * combining the following tag lists:
     183              :  *   - `Metavariables::const_global_cache_tags` which should contain only those
     184              :  *     tags that cannot be added from the other tag lists below.
     185              :  *   - `Component::const_global_cache_tags` for each `Component` in
     186              :  *     `Metavariables::component_list` which should contain the tags needed by
     187              :  *     any simple actions called on the Component, as well as tags need by the
     188              :  *     `allocate_array` function of an array component.  The type alias may be
     189              :  *     omitted for an empty list.
     190              :  *   - `Action::const_global_cache_tags` for each `Action` in the
     191              :  *     `phase_dependent_action_list` of each `Component` of
     192              :  *     `Metavariables::component_list` which should contain the tags needed by
     193              :  *     that  Action.  The type alias may be omitted for an empty list.
     194              :  *
     195              :  * The tag lists for the mutable items added to the GlobalCache is created
     196              :  * by combining exactly the same tag lists as for the const items, except with
     197              :  * `const_global_cache_tags` replaced by `mutable_global_cache_tags`.
     198              :  *
     199              :  * The tags in the `const_global_cache_tags` and
     200              :  * `mutable_global_cache_tags` type lists are db::SimpleTag%s that
     201              :  * have a `using option_tags` type alias and a static function
     202              :  * `create_from_options` that are used to create the constant data (or initial
     203              :  * mutable data) from input file options.
     204              :  *
     205              :  * References to const items in the GlobalCache are also added to the
     206              :  * db::DataBox of each `Component` in the
     207              :  * `Metavariables::component_list` with the same tag with which they
     208              :  * were inserted into the GlobalCache.  References to mutable items
     209              :  * in the GlobalCache are not added to the db::DataBox.
     210              :  *
     211              :  * Since mutable data is stored once per Charm++ node, we require that
     212              :  * data structures held by mutable tags have some sort of thread-safety.
     213              :  * Particularly, we require data structures in mutable tags be Single
     214              :  * Producer-Multiple Consumer. This means that the data structure should be
     215              :  * readable/accessible by multiple threads at once, even while being mutated
     216              :  * (multiple consumer), but will not be edited/mutated simultaneously on
     217              :  * multiple threads (single producer).
     218              :  */
     219              : template <typename Metavariables>
     220            1 : class GlobalCache
     221              :     : public std::conditional_t<GlobalCache_detail::mock_global_cache,
     222              :                                 GlobalCache_detail::MockGlobalCache,
     223              :                                 CBase_GlobalCache<Metavariables>> {
     224            0 :   using Base = std::conditional_t<GlobalCache_detail::mock_global_cache,
     225              :                                   GlobalCache_detail::MockGlobalCache,
     226              :                                   CBase_GlobalCache<Metavariables>>;
     227            0 :   using parallel_component_tag_list = tmpl::transform<
     228              :       typename Metavariables::component_list,
     229              :       tmpl::bind<
     230              :           tmpl::type_,
     231              :           tmpl::bind<Parallel::proxy_from_parallel_component, tmpl::_1>>>;
     232            0 :   using ParallelComponentTuple =
     233              :       tuples::tagged_tuple_from_typelist<parallel_component_tag_list>;
     234              : 
     235              :  public:
     236            0 :   static constexpr bool is_mocked = GlobalCache_detail::mock_global_cache;
     237            0 :   using proxy_type = CProxy_GlobalCache<Metavariables>;
     238            0 :   using main_proxy_type = CProxy_Main<Metavariables>;
     239              :   /// Access to the Metavariables template parameter
     240            1 :   using metavariables = Metavariables;
     241              :   /// Typelist of the ParallelComponents stored in the GlobalCache
     242            1 :   using component_list = typename Metavariables::component_list;
     243              :   // Even though the GlobalCache doesn't run the Algorithm, this type alias
     244              :   // helps in identifying that the GlobalCache is a Nodegroup using
     245              :   // Parallel::is_nodegroup_v
     246            0 :   using chare_type = Parallel::Algorithms::Nodegroup;
     247            0 :   using const_tags_list = get_const_global_cache_tags<Metavariables>;
     248            0 :   using ConstTagsTuple = tuples::tagged_tuple_from_typelist<const_tags_list>;
     249            0 :   using ConstTagsStorage = ConstTagsTuple;
     250            0 :   using mutable_tags_list = get_mutable_global_cache_tags<Metavariables>;
     251            0 :   using MutableTagsTuple =
     252              :       tuples::tagged_tuple_from_typelist<mutable_tags_list>;
     253            0 :   using MutableTagsStorage = tuples::tagged_tuple_from_typelist<
     254              :       get_mutable_global_cache_tag_storage<Metavariables>>;
     255              : 
     256              :   /// Constructor meant to be used in the ActionTesting framework.
     257            1 :   GlobalCache(ConstTagsTuple const_global_cache,
     258              :               MutableTagsTuple mutable_global_cache = {},
     259              :               std::vector<size_t> procs_per_node = {1}, const int my_proc = 0,
     260              :               const int my_node = 0, const int my_local_rank = 0);
     261              : 
     262              :   /// Constructor meant to be used in charm-aware settings (with a Main proxy).
     263            1 :   GlobalCache(ConstTagsTuple const_global_cache,
     264              :               MutableTagsTuple mutable_global_cache,
     265              :               std::optional<main_proxy_type> main_proxy);
     266              : 
     267            0 :   explicit GlobalCache(CkMigrateMessage* msg) : Base(msg) {}
     268              : 
     269            0 :   ~GlobalCache() override {
     270              :     (void)Parallel::charmxx::RegisterChare<
     271              :         GlobalCache<Metavariables>,
     272              :         CkIndex_GlobalCache<Metavariables>>::registrar;
     273              :   }
     274              :   /// \cond
     275              :   GlobalCache() = default;
     276              :   GlobalCache(const GlobalCache&) = default;
     277              :   GlobalCache& operator=(const GlobalCache&) = default;
     278              :   GlobalCache(GlobalCache&&) = default;
     279              :   GlobalCache& operator=(GlobalCache&&) = default;
     280              :   /// \endcond
     281              : 
     282              :   /// Entry method to set the ParallelComponents (should only be called once)
     283            1 :   void set_parallel_components(ParallelComponentTuple&& parallel_components,
     284              :                                const CkCallback& callback);
     285              : 
     286              :   /*!
     287              :    * \brief Returns whether the object referred to by `GlobalCacheTag`
     288              :    * (which must be a mutable cache tag) is ready to be accessed by a
     289              :    * `get` call.
     290              :    *
     291              :    * \details `function` is a user-defined invokable that:
     292              :    * - takes one argument: a const reference to the object referred to by the
     293              :    *   `GlobalCacheTag`.
     294              :    * - if the data is ready, returns a default constructed
     295              :    *   `std::unique_ptr<CallBack>`
     296              :    * - if the data is not ready, returns a `std::unique_ptr<CallBack>`,
     297              :    *   where the `Callback` will re-invoke the current action on the
     298              :    *   current parallel component. This callback should be a
     299              :    *   `Parallel::PerformAlgorithmCallback`. Other types of callbacks are not
     300              :    *   supported at this time.
     301              :    *
     302              :    * \parblock
     303              :    * \warning The `function` may be called twice so it should not modify any
     304              :    * state in its scope.
     305              :    * \endparblock
     306              :    *
     307              :    * \parblock
     308              :    * \warning If there has already been a callback registered for the given
     309              :    * `array_component_id`, then the callback returned by `function` will **not**
     310              :    * be registered or called.
     311              :    * \endparblock
     312              :    */
     313              :   template <typename GlobalCacheTag, typename Function>
     314            1 :   bool mutable_cache_item_is_ready(
     315              :       const Parallel::ArrayComponentId& array_component_id,
     316              :       const Function& function);
     317              : 
     318              :   /// Mutates the non-const object identified by GlobalCacheTag.
     319              :   /// \requires `GlobalCacheTag` is a tag in `mutable_global_cache_tags`
     320              :   /// defined by the Metavariables and in Actions.
     321              :   ///
     322              :   /// Internally calls `Function::apply()`, where `Function` is a
     323              :   /// user-defined struct and `Function::apply()` is a user-defined
     324              :   /// static function that mutates the object.  `Function::apply()`
     325              :   /// takes as its first argument a `gsl::not_null` pointer to the
     326              :   /// object named by the GlobalCacheTag (or if that object is a
     327              :   /// `std::unique_ptr<T>`, a `gsl::not_null<T*>`), and takes the contents of
     328              :   /// `args` as subsequent arguments.
     329              :   template <typename GlobalCacheTag, typename Function, typename... Args>
     330            1 :   void mutate(const std::tuple<Args...>& args);
     331              : 
     332              :   /// Entry method that computes the size of the local branch of the
     333              :   /// GlobalCache and sends it to the MemoryMonitor parallel component.
     334              :   ///
     335              :   /// \note This can only be called if the MemoryMonitor component is in the
     336              :   /// `component_list` of the metavariables. Also can't be called in the testing
     337              :   /// framework. Trying to do either of these will result in an ERROR.
     338            1 :   void compute_size_for_memory_monitor(const double time);
     339              : 
     340              :   /// Entry method that will set the value of the Parallel::Tags::ResourceInfo
     341              :   /// tag to the value passed in (if the tag exists in the GlobalCache)
     342              :   ///
     343              :   /// This is only meant to be called once.
     344            1 :   void set_resource_info(
     345              :       const Parallel::ResourceInfo<Metavariables>& resource_info);
     346              : 
     347              :   /// Entry method to print the mutable global cache callbacks upon a deadlock
     348              :   /// to a file.
     349              :   ///
     350              :   /// For each mutable cache tag, this prints
     351              :   ///
     352              :   /// - Tag name
     353              :   /// - Node number
     354              :   /// - Number of callbacks registered
     355              :   /// - For each callback, the ArrayComponentId and the callback name
     356            1 :   void print_mutable_cache_callbacks(const std::string& file_name);
     357              : 
     358              :   /// Retrieve the resource_info
     359            1 :   const Parallel::ResourceInfo<Metavariables>& get_resource_info() const {
     360              :     return resource_info_;
     361              :   }
     362              : 
     363              :   /// Overlay new data onto a subset of the GlobalCache's tags.
     364              :   ///
     365              :   /// This is used when reparsing an input file to update option values during
     366              :   /// a restart from a checkpoint.
     367            1 :   void overlay_cache_data(tuples::tagged_tuple_from_typelist<
     368              :                           Parallel::get_overlayable_tag_list<Metavariables>>
     369              :                               data_to_overlay);
     370              : 
     371              :   /// Retrieve the proxy to the global cache
     372            1 :   proxy_type get_this_proxy();
     373              : 
     374            0 :   void pup(PUP::er& p) override;  // NOLINT
     375              : 
     376              :   /// Retrieve the proxy to the Main chare (or std::nullopt if the proxy has not
     377              :   /// been set, i.e. we are not charm-aware).
     378            1 :   std::optional<main_proxy_type> get_main_proxy();
     379              : 
     380              :   /// @{
     381              :   /// Wrappers for charm++ informational functions.
     382              : 
     383              :   /// Number of processing elements
     384            1 :   int number_of_procs() const;
     385              :   /// Number of nodes.
     386            1 :   int number_of_nodes() const;
     387              :   /// Number of processing elements on the given node.
     388            1 :   int procs_on_node(const int node_index) const;
     389              :   /// %Index of first processing element on the given node.
     390            1 :   int first_proc_on_node(const int node_index) const;
     391              :   /// %Index of the node for the given processing element.
     392            1 :   int node_of(const int proc_index) const;
     393              :   /// The local index for the given processing element on its node.
     394            1 :   int local_rank_of(const int proc_index) const;
     395              :   /// %Index of my processing element.
     396            1 :   int my_proc() const;
     397              :   /// %Index of my node.
     398            1 :   int my_node() const;
     399              :   /// The local index of my processing element on my node.
     400              :   /// This is in the interval 0, ..., procs_on_node(my_node()) - 1.
     401            1 :   int my_local_rank() const;
     402              :   /// @}
     403              : 
     404              :  private:
     405              :   // clang-tidy: false positive, redundant declaration
     406              :   template <typename GlobalCacheTag, typename MV>
     407            0 :   friend auto get(const GlobalCache<MV>& cache)  // NOLINT
     408              :       -> const GlobalCache_detail::type_for_get<GlobalCacheTag, MV>&;
     409              : 
     410              :   // clang-tidy: false positive, redundant declaration
     411              :   template <typename ParallelComponentTag, typename MV>
     412            0 :   friend auto get_parallel_component(  // NOLINT
     413              :       GlobalCache<MV>& cache)
     414              :       -> Parallel::proxy_from_parallel_component<
     415              :           GlobalCache_detail::get_component_if_mocked<
     416              :               typename MV::component_list, ParallelComponentTag>>&;
     417              : 
     418              :   // clang-tidy: false positive, redundant declaration
     419              :   template <typename ParallelComponentTag, typename MV>
     420            0 :   friend auto get_parallel_component(  // NOLINT
     421              :       const GlobalCache<MV>& cache)
     422              :       -> const Parallel::proxy_from_parallel_component<
     423              :           GlobalCache_detail::get_component_if_mocked<
     424              :               typename MV::component_list,
     425              :               ParallelComponentTag>>&;  // NOLINT
     426              : 
     427            0 :   ConstTagsStorage const_global_cache_{};
     428            0 :   MutableTagsStorage mutable_global_cache_{};
     429              :   // Wrap mutable tags in Parallel::MutexTag. The type of MutexTag is a
     430              :   // pair<mutex, mutex>. The first mutex is for editing the value of the mutable
     431              :   // tag. The second mutex is for editing the vector of callbacks associated
     432              :   // with the mutable tag.
     433              :   tuples::tagged_tuple_from_typelist<
     434              :       tmpl::transform<MutableTagsStorage, tmpl::bind<MutexTag, tmpl::_1>>>
     435            0 :       mutexes_{};
     436            0 :   ParallelComponentTuple parallel_components_{};
     437            0 :   Parallel::ResourceInfo<Metavariables> resource_info_{};
     438            0 :   bool parallel_components_have_been_set_{false};
     439            0 :   bool resource_info_has_been_set_{false};
     440            0 :   std::optional<main_proxy_type> main_proxy_;
     441              :   // Defaults for testing framework
     442            0 :   int my_proc_{0};
     443            0 :   int my_node_{0};
     444            0 :   int my_local_rank_{0};
     445            0 :   std::vector<size_t> procs_per_node_{1};
     446              : };
     447              : 
     448              : template <typename Metavariables>
     449              : GlobalCache<Metavariables>::GlobalCache(ConstTagsTuple const_global_cache,
     450              :                                         MutableTagsTuple mutable_global_cache,
     451              :                                         std::vector<size_t> procs_per_node,
     452              :                                         const int my_proc, const int my_node,
     453              :                                         const int my_local_rank)
     454              :     : const_global_cache_(std::move(const_global_cache)),
     455              :       mutable_global_cache_(GlobalCache_detail::make_mutable_cache_tag_storage(
     456              :           std::move(mutable_global_cache))),
     457              :       main_proxy_(std::nullopt),
     458              :       my_proc_(my_proc),
     459              :       my_node_(my_node),
     460              :       my_local_rank_(my_local_rank),
     461              :       procs_per_node_(std::move(procs_per_node)) {}
     462              : 
     463              : template <typename Metavariables>
     464              : GlobalCache<Metavariables>::GlobalCache(
     465              :     ConstTagsTuple const_global_cache, MutableTagsTuple mutable_global_cache,
     466              :     std::optional<main_proxy_type> main_proxy)
     467              :     : const_global_cache_(std::move(const_global_cache)),
     468              :       mutable_global_cache_(GlobalCache_detail::make_mutable_cache_tag_storage(
     469              :           std::move(mutable_global_cache))),
     470              :       main_proxy_(std::move(main_proxy)) {}
     471              : 
     472              : template <typename Metavariables>
     473              : void GlobalCache<Metavariables>::set_parallel_components(
     474              :     ParallelComponentTuple&& parallel_components, const CkCallback& callback) {
     475              :   ASSERT(!parallel_components_have_been_set_,
     476              :          "Can only set the parallel_components once");
     477              :   parallel_components_ = std::move(parallel_components);
     478              :   parallel_components_have_been_set_ = true;
     479              :   this->contribute(callback);
     480              : }
     481              : 
     482              : template <typename Metavariables>
     483              : template <typename GlobalCacheTag, typename Function>
     484              : bool GlobalCache<Metavariables>::mutable_cache_item_is_ready(
     485              :     const Parallel::ArrayComponentId& array_component_id,
     486              :     const Function& function) {
     487              :   using tag = MutableCacheTag<GlobalCache_detail::get_matching_mutable_tag<
     488              :       GlobalCacheTag, Metavariables>>;
     489              :   std::unique_ptr<Callback> optional_callback{};
     490              :   // Returns true if a callback was returned from `function`. Returns false if
     491              :   // nullptr was returned
     492              :   const auto callback_was_registered = [this, &function,
     493              :                                         &optional_callback]() -> bool {
     494              :     // Reads don't need a lock.
     495              :     if constexpr (tt::is_a_v<std::unique_ptr, typename tag::tag::type>) {
     496              :       optional_callback =
     497              :           function(*(std::get<0>(tuples::get<tag>(mutable_global_cache_))));
     498              :     } else {
     499              :       optional_callback =
     500              :           function(std::get<0>(tuples::get<tag>(mutable_global_cache_)));
     501              :     }
     502              : 
     503              :     return optional_callback != nullptr;
     504              :   };
     505              : 
     506              :   if (callback_was_registered()) {
     507              :     optional_callback->register_with_charm();
     508              :     // Second mutex is for vector of callbacks
     509              :     std::mutex& mutex = tuples::get<MutexTag<tag>>(mutexes_).second;
     510              :     const std::unique_ptr<Callback> clone_of_optional_callback =
     511              :         optional_callback->get_clone();
     512              :     {
     513              :       // Scoped for lock guard
     514              :       const std::lock_guard<std::mutex> lock(mutex);
     515              :       std::unordered_map<Parallel::ArrayComponentId,
     516              :                          std::vector<std::unique_ptr<Callback>>>& callbacks =
     517              :           std::get<1>(tuples::get<tag>(mutable_global_cache_));
     518              : 
     519              :       if (callbacks.contains(array_component_id)) {
     520              :         // If this array component id already exists, we don't want to add
     521              :         // multiple of the same callback, so we loop over the existing callbacks
     522              :         // and only if none of the existing callbacks are equal to the optional
     523              :         // callback do we move the optional callback into the vector
     524              :         auto& vec_callbacks = callbacks.at(array_component_id);
     525              :         if (alg::none_of(vec_callbacks,
     526              :                          [&](const std::unique_ptr<Callback>& local_callback) {
     527              :                            return local_callback->is_equal_to(
     528              :                                *optional_callback);
     529              :                          })) {
     530              :           vec_callbacks.emplace_back(std::move(optional_callback));
     531              :         }
     532              :       } else {
     533              :         // If we don't have this array component id, then we create the vector
     534              :         // and move the optional callback into the vector
     535              :         callbacks[array_component_id] =
     536              :             std::vector<std::unique_ptr<Callback>>(1);
     537              :         callbacks.at(array_component_id)[0] = std::move(optional_callback);
     538              :       }
     539              :     }
     540              : 
     541              :     // We must check if the tag is ready again. Consider the following example:
     542              :     //
     543              :     // We have two writers, A and B preparing to make independent changes to a
     544              :     // cache object. We have an element E with a callback waiting for the change
     545              :     // B is going to make. Suppose the following sequence of events:
     546              :     //
     547              :     // 1. A mutates the object, copies the callback list (below in `mutate`),
     548              :     //    and starts the callback for element E.
     549              :     // 2. E checks the current value and determines it is not ready.
     550              :     // 3. B mutates the object, copies the empty callback list, and returns with
     551              :     //    nothing to do.
     552              :     // 4. E returns a new callback, which is added to the callback list.
     553              :     // 5. A returns.
     554              :     //
     555              :     // We now have E waiting on a change that has already happened. This will
     556              :     // most certainly result in a deadlock if E is blocking the Algorithm. Thus
     557              :     // we must do another check for whether the cache object is ready or not. In
     558              :     // the order of events, this check happens sometime after 4. When this check
     559              :     // happens, E concludes that the cache object *is* ready (because 3 is when
     560              :     // the object was mutated) and E can continue on.
     561              :     //
     562              :     // If this second check reveals that the object *is* ready, then we have an
     563              :     // unecessary callback in our map, so we remove it.
     564              :     //
     565              :     // If this second check reveals that the object *isn't* ready, then we don't
     566              :     // bother adding another callback to the map because one already exists. No
     567              :     // need to call a callback twice.
     568              :     //
     569              :     // This function returns true if no callback was registered and false if one
     570              :     // was registered.
     571              :     const bool cache_item_is_ready = not callback_was_registered();
     572              :     if (cache_item_is_ready) {
     573              :       const std::lock_guard<std::mutex> lock(mutex);
     574              :       std::unordered_map<Parallel::ArrayComponentId,
     575              :                          std::vector<std::unique_ptr<Callback>>>& callbacks =
     576              :           std::get<1>(tuples::get<tag>(mutable_global_cache_));
     577              : 
     578              :       // It's possible that no new callbacks were registered, so make sure this
     579              :       // array component id still has callbacks before trying to remove them.
     580              :       if (callbacks.contains(array_component_id)) {
     581              :         // If this callback was a duplicate, we'll have to search through all
     582              :         // callbacks to determine which to remove. If it wasn't a duplicate,
     583              :         // then it'll just be the last callback in the vector.
     584              :         auto& vec_callbacks = callbacks.at(array_component_id);
     585              :         std::erase_if(vec_callbacks,
     586              :                       [&clone_of_optional_callback](const auto& t) {
     587              :                         return t->is_equal_to(*clone_of_optional_callback);
     588              :                       });
     589              : 
     590              :         if (callbacks.at(array_component_id).empty()) {
     591              :           callbacks.erase(array_component_id);
     592              :         }
     593              :       }
     594              :     }
     595              : 
     596              :     return cache_item_is_ready;
     597              :   } else {
     598              :     // The user-defined `function` didn't specify a callback, which
     599              :     // means that the item is ready.
     600              :     return true;
     601              :   }
     602              : }
     603              : 
     604              : template <typename Metavariables>
     605              : template <typename GlobalCacheTag, typename Function, typename... Args>
     606              : void GlobalCache<Metavariables>::mutate(const std::tuple<Args...>& args) {
     607              :   (void)Parallel::charmxx::RegisterGlobalCacheMutate<
     608              :       Metavariables, GlobalCacheTag, Function, Args...>::registrar;
     609              :   using tag = MutableCacheTag<GlobalCache_detail::get_matching_mutable_tag<
     610              :       GlobalCacheTag, Metavariables>>;
     611              : 
     612              :   // Do the mutate.
     613              :   std::apply(
     614              :       [this](const auto&... local_args) {
     615              :         // First mutex is for value of mutable tag
     616              :         std::mutex& mutex = tuples::get<MutexTag<tag>>(mutexes_).first;
     617              :         const std::lock_guard<std::mutex> lock(mutex);
     618              :         Function::apply(make_not_null(&StdHelpers::retrieve(std::get<0>(
     619              :                             tuples::get<tag>(mutable_global_cache_)))),
     620              :                         local_args...);
     621              :       },
     622              :       args);
     623              : 
     624              :   // A callback might call mutable_cache_item_is_ready, which might add yet
     625              :   // another callback to the vector of callbacks.  We don't want to immediately
     626              :   // invoke this new callback as it might just add another callback again (and
     627              :   // again in an infinite loop). And we don't want to remove it from the map of
     628              :   // callbacks before it is invoked otherwise we could get a deadlock.
     629              :   // Therefore, after locking it, we std::move the map of callbacks into a
     630              :   // temporary map, clear the original map, and invoke the callbacks in the
     631              :   // temporary map.
     632              :   std::unordered_map<Parallel::ArrayComponentId,
     633              :                      std::vector<std::unique_ptr<Callback>>>
     634              :       callbacks{};
     635              :   // Second mutex is for map of callbacks
     636              :   std::mutex& mutex = tuples::get<MutexTag<tag>>(mutexes_).second;
     637              :   {
     638              :     // Scoped for lock guard
     639              :     const std::lock_guard<std::mutex> lock(mutex);
     640              :     callbacks = std::move(std::get<1>(tuples::get<tag>(mutable_global_cache_)));
     641              :     std::get<1>(tuples::get<tag>(mutable_global_cache_)).clear();
     642              :   }
     643              : 
     644              :   // Invoke the callbacks.  Any new callbacks that are added to the
     645              :   // list (if a callback calls mutable_cache_item_is_ready) will be
     646              :   // saved and will not be invoked here.
     647              :   for (auto& [array_component_id, vec_callbacks] : callbacks) {
     648              :     for (auto& callback : vec_callbacks) {
     649              :       callback->invoke();
     650              :     }
     651              :   }
     652              : }
     653              : 
     654              : template <typename Metavariables>
     655              : void GlobalCache<Metavariables>::overlay_cache_data(
     656              :     tuples::tagged_tuple_from_typelist<
     657              :         Parallel::get_overlayable_tag_list<Metavariables>>
     658              :         data_to_overlay) {
     659              :   tmpl::for_each<Parallel::get_overlayable_tag_list<Metavariables>>(
     660              :       [this, &data_to_overlay]<typename Tag>(tmpl::type_<Tag> /*meta*/) {
     661              :         tuples::get<Tag>(const_global_cache_) =
     662              :             std::move(tuples::get<Tag>(data_to_overlay));
     663              :       });
     664              : }
     665              : 
     666              : #if defined(__GNUC__) && !defined(__clang__)
     667              : #pragma GCC diagnostic push
     668              : #pragma GCC diagnostic ignored "-Wsuggest-attribute=noreturn"
     669              : #endif  // defined(__GNUC__) && !defined(__clang__)
     670              : template <typename Metavariables>
     671              : void GlobalCache<Metavariables>::compute_size_for_memory_monitor(
     672              :     const double time) {
     673              :   if constexpr (tmpl::list_contains_v<
     674              :                     typename Metavariables::component_list,
     675              :                     mem_monitor::MemoryMonitor<Metavariables>>) {
     676              :     const double size_in_bytes =
     677              :         static_cast<double>(size_of_object_in_bytes(*this));
     678              :     const double size_in_MB = size_in_bytes / 1.0e6;
     679              : 
     680              :     auto& mem_monitor_proxy = Parallel::get_parallel_component<
     681              :         mem_monitor::MemoryMonitor<Metavariables>>(*this);
     682              : 
     683              :     const int my_node = Parallel::my_node<int>(*this);
     684              : 
     685              :     Parallel::simple_action<
     686              :         mem_monitor::ContributeMemoryData<GlobalCache<Metavariables>>>(
     687              :         mem_monitor_proxy, time, my_node, size_in_MB);
     688              :   } else {
     689              :     (void)time;
     690              :     ERROR(
     691              :         "GlobalCache::compute_size_for_memory_monitor can only be called if "
     692              :         "the MemoryMonitor is in the component list in the metavariables.\n");
     693              :   }
     694              : }
     695              : 
     696              : template <typename Metavariables>
     697              : void GlobalCache<Metavariables>::set_resource_info(
     698              :     const Parallel::ResourceInfo<Metavariables>& resource_info) {
     699              :   ASSERT(not resource_info_has_been_set_,
     700              :          "Can only set the resource info once");
     701              :   resource_info_ = resource_info;
     702              :   resource_info_has_been_set_ = true;
     703              : }
     704              : 
     705              : template <typename Metavariables>
     706              : void GlobalCache<Metavariables>::print_mutable_cache_callbacks(
     707              :     const std::string& file_name) {
     708              :   std::stringstream ss{};
     709              :   ss << std::setprecision(std::numeric_limits<double>::digits10 + 4)
     710              :      << std::scientific;
     711              : 
     712              :   ss << "========== BEGIN CALLBACKS ON NODE " << this->my_node()
     713              :      << " ==========\n";
     714              : 
     715              :   tmpl::for_each<typename MutableTagsStorage::tags_list>(
     716              :       [this, &ss](auto tag_v) {
     717              :         using Tag = tmpl::type_from<decltype(tag_v)>;
     718              : 
     719              :         const auto& callbacks =
     720              :             std::get<1>(tuples::get<Tag>(mutable_global_cache_));
     721              : 
     722              :         ss << "For tag " << pretty_type::name<typename Tag::tag>()
     723              :            << ", callbacks ("
     724              :            << alg::accumulate(callbacks, 0_st,
     725              :                               [](const size_t cur_size, const auto& v) {
     726              :                                 return cur_size + v.second.size();
     727              :                               })
     728              :            << "):\n";
     729              : 
     730              :         for (const auto& [array_component_id, vec_callbacks] : callbacks) {
     731              :           for (const auto& callback : vec_callbacks) {
     732              :             ss << "  ArrayComponentId " << array_component_id << ": "
     733              :                << callback->name() << "\n";
     734              :           }
     735              :         }
     736              :       });
     737              : 
     738              :   ss << "========== END CALLBACKS ON NODE " << this->my_node()
     739              :      << " ============\n";
     740              : 
     741              :   Parallel::fprintf(file_name, "%s\n", ss.str());
     742              : }
     743              : 
     744              : #if defined(__GNUC__) && !defined(__clang__)
     745              : #pragma GCC diagnostic pop
     746              : #endif  // defined(__GNUC__) && !defined(__clang__)
     747              : 
     748              : template <typename Metavariables>
     749              : typename Parallel::GlobalCache<Metavariables>::proxy_type
     750              : GlobalCache<Metavariables>::get_this_proxy() {
     751              :   if constexpr (is_mocked) {
     752              :     // The proxy is not used in the testing framework
     753              :     return Parallel::GlobalCache<Metavariables>::proxy_type{};
     754              :   } else {
     755              :     return this->thisProxy;
     756              :   }
     757              : }
     758              : 
     759              : template <typename Metavariables>
     760              : std::optional<typename Parallel::GlobalCache<Metavariables>::main_proxy_type>
     761              : GlobalCache<Metavariables>::get_main_proxy() {
     762              :   return main_proxy_;
     763              : }
     764              : 
     765              : // For all these functions, if the main proxy is set (meaning we are
     766              : // charm-aware) then just call the sys:: functions. Otherwise, use the values
     767              : // set for the testing framework (or the defaults).
     768              : template <typename Metavariables>
     769              : int GlobalCache<Metavariables>::number_of_procs() const {
     770              :   return main_proxy_.has_value()
     771              :              ? sys::number_of_procs()
     772              :              : static_cast<int>(alg::accumulate(procs_per_node_, 0_st));
     773              : }
     774              : 
     775              : template <typename Metavariables>
     776              : int GlobalCache<Metavariables>::number_of_nodes() const {
     777              :   return main_proxy_.has_value() ? sys::number_of_nodes()
     778              :                                  : static_cast<int>(procs_per_node_.size());
     779              : }
     780              : 
     781              : template <typename Metavariables>
     782              : int GlobalCache<Metavariables>::procs_on_node(const int node_index) const {
     783              :   return main_proxy_.has_value()
     784              :              ? sys::procs_on_node(node_index)
     785              :              : static_cast<int>(
     786              :                    procs_per_node_[static_cast<size_t>(node_index)]);
     787              : }
     788              : 
     789              : template <typename Metavariables>
     790              : int GlobalCache<Metavariables>::first_proc_on_node(const int node_index) const {
     791              :   return main_proxy_.has_value()
     792              :              ? sys::first_proc_on_node(node_index)
     793              :              : static_cast<int>(
     794              :                    std::accumulate(procs_per_node_.begin(),
     795              :                                    procs_per_node_.begin() + node_index, 0_st));
     796              : }
     797              : 
     798              : template <typename Metavariables>
     799              : int GlobalCache<Metavariables>::node_of(const int proc_index) const {
     800              :   if (main_proxy_.has_value()) {
     801              :     // For some reason gcov doesn't think this line is tested even though it is
     802              :     // in Test_AlgorithmGlobalCache.cpp
     803              :     return sys::node_of(proc_index);  // LCOV_EXCL_LINE
     804              :   } else {
     805              :     size_t procs_so_far = 0;
     806              :     size_t node = 0;
     807              :     while (procs_so_far <= static_cast<size_t>(proc_index)) {
     808              :       procs_so_far += procs_per_node_[node];
     809              :       ++node;
     810              :     }
     811              :     return static_cast<int>(--node);
     812              :   }
     813              : }
     814              : 
     815              : template <typename Metavariables>
     816              : int GlobalCache<Metavariables>::local_rank_of(const int proc_index) const {
     817              :   return main_proxy_.has_value()
     818              :              ? sys::local_rank_of(proc_index)
     819              :              : proc_index - first_proc_on_node(node_of(proc_index));
     820              : }
     821              : 
     822              : template <typename Metavariables>
     823              : int GlobalCache<Metavariables>::my_proc() const {
     824              :   return main_proxy_.has_value() ? sys::my_proc() : my_proc_;
     825              : }
     826              : 
     827              : template <typename Metavariables>
     828              : int GlobalCache<Metavariables>::my_node() const {
     829              :   return main_proxy_.has_value() ? sys::my_node() : my_node_;
     830              : }
     831              : 
     832              : template <typename Metavariables>
     833              : int GlobalCache<Metavariables>::my_local_rank() const {
     834              :   return main_proxy_.has_value() ? sys::my_local_rank() : my_local_rank_;
     835              : }
     836              : 
     837              : template <typename Metavariables>
     838              : void GlobalCache<Metavariables>::pup(PUP::er& p) {
     839              :   p | const_global_cache_;
     840              :   p | parallel_components_;
     841              :   p | mutable_global_cache_;
     842              :   p | main_proxy_;
     843              :   p | parallel_components_have_been_set_;
     844              :   p | resource_info_has_been_set_;
     845              :   p | my_proc_;
     846              :   p | my_node_;
     847              :   p | my_local_rank_;
     848              :   p | procs_per_node_;
     849              : }
     850              : 
     851              : /// @{
     852              : /// \ingroup ParallelGroup
     853              : /// \brief Access the Charm++ proxy associated with a ParallelComponent
     854              : ///
     855              : /// \requires ParallelComponentTag is a tag in component_list
     856              : ///
     857              : /// \returns a Charm++ proxy that can be used to call an entry method on the
     858              : /// chare(s)
     859              : template <typename ParallelComponentTag, typename Metavariables>
     860            1 : auto get_parallel_component(GlobalCache<Metavariables>& cache)
     861              :     -> Parallel::proxy_from_parallel_component<
     862              :         GlobalCache_detail::get_component_if_mocked<
     863              :             typename Metavariables::component_list, ParallelComponentTag>>& {
     864              :   return tuples::get<tmpl::type_<Parallel::proxy_from_parallel_component<
     865              :       GlobalCache_detail::get_component_if_mocked<
     866              :           typename Metavariables::component_list, ParallelComponentTag>>>>(
     867              :       cache.parallel_components_);
     868              : }
     869              : 
     870              : template <typename ParallelComponentTag, typename Metavariables>
     871            1 : auto get_parallel_component(const GlobalCache<Metavariables>& cache)
     872              :     -> const Parallel::proxy_from_parallel_component<
     873              :         GlobalCache_detail::get_component_if_mocked<
     874              :             typename Metavariables::component_list, ParallelComponentTag>>& {
     875              :   return tuples::get<tmpl::type_<Parallel::proxy_from_parallel_component<
     876              :       GlobalCache_detail::get_component_if_mocked<
     877              :           typename Metavariables::component_list, ParallelComponentTag>>>>(
     878              :       cache.parallel_components_);
     879              : }
     880              : /// @}
     881              : 
     882              : /// @{
     883              : /// \ingroup ParallelGroup
     884              : /// \brief Access data in the cache
     885              : ///
     886              : /// \requires GlobalCacheTag is a tag in the `mutable_global_cache_tags`
     887              : /// or `const_global_cache_tags` defined by the Metavariables and in Actions.
     888              : ///
     889              : /// \returns a constant reference to an object in the cache
     890              : template <typename GlobalCacheTag, typename Metavariables>
     891            1 : auto get(const GlobalCache<Metavariables>& cache)
     892              :     -> const GlobalCache_detail::type_for_get<GlobalCacheTag, Metavariables>& {
     893              :   constexpr bool is_mutable =
     894              :       is_in_mutable_global_cache<Metavariables, GlobalCacheTag>;
     895              :   // We check if the tag is to be retrieved directly or via a base class
     896              :   using tmp_tag =
     897              :       GlobalCache_detail::get_matching_tag<GlobalCacheTag, Metavariables>;
     898              :   using tag =
     899              :       tmpl::conditional_t<is_mutable, MutableCacheTag<tmp_tag>, tmp_tag>;
     900              :   if constexpr (is_mutable) {
     901              :     // Tag is not in the const tags, so use mutable_global_cache_. No locks here
     902              :     // because we require all mutable tags to be able to be read at all times
     903              :     // (even when being written to)
     904              :     if constexpr (tt::is_a_v<std::unique_ptr, typename tag::tag::type>) {
     905              :       return *std::get<0>(tuples::get<tag>(cache.mutable_global_cache_));
     906              :     } else {
     907              :       return std::get<0>(tuples::get<tag>(cache.mutable_global_cache_));
     908              :     }
     909              :   } else {
     910              :     // Tag is in the const tags, so use const_global_cache_
     911              :     if constexpr (tt::is_a_v<std::unique_ptr, typename tag::type>) {
     912              :       return *(tuples::get<tag>(cache.const_global_cache_));
     913              :     } else {
     914              :       return tuples::get<tag>(cache.const_global_cache_);
     915              :     }
     916              :   }
     917              : }
     918              : 
     919              : /// \ingroup ParallelGroup
     920              : /// \brief Returns whether the object identified by `GlobalCacheTag`
     921              : /// is ready to be accessed by `get`.
     922              : ///
     923              : /// \requires `GlobalCacheTag` is a tag in `mutable_global_cache_tags`
     924              : /// defined by the Metavariables and in Actions.
     925              : ///
     926              : /// \requires `function` is a user-defined invokable that takes one argument:
     927              : /// a const reference to the object referred to by the
     928              : /// `GlobalCacheTag`.  `function` returns a
     929              : /// `std::unique_ptr<CallBack>` that determines the readiness. To
     930              : /// indicate that the item is ready, the `std::unique_ptr` returned
     931              : /// by `function` must be nullptr; in this case
     932              : /// `mutable_cache_item_is_ready` returns true. To indicate that the
     933              : /// item is not ready, the `std::unique_ptr` returned by `function`
     934              : /// must be valid; in this case, `mutable_cache_item_is_ready`
     935              : /// appends the `std::unique_ptr<Callback>` to an
     936              : /// internal list of callbacks to be called on `mutate`, and then
     937              : /// returns false.
     938              : ///
     939              : /// \note If `function` is returning a valid callback, it should only return a
     940              : /// `Parallel::PerformAlgorithmCallback`. Other types of callbacks are not
     941              : /// supported at this time.
     942              : template <typename GlobalCacheTag, typename Function, typename Metavariables>
     943            1 : bool mutable_cache_item_is_ready(
     944              :     GlobalCache<Metavariables>& cache,
     945              :     const Parallel::ArrayComponentId& array_component_id,
     946              :     const Function& function) {
     947              :   return cache.template mutable_cache_item_is_ready<GlobalCacheTag>(
     948              :       array_component_id, function);
     949              : }
     950              : 
     951              : /// \ingroup ParallelGroup
     952              : ///
     953              : /// \brief Mutates non-const data in the cache, by calling `Function::apply()`
     954              : ///
     955              : /// \requires `GlobalCacheTag` is a tag in tag_list.
     956              : /// \requires `Function` is a struct with a static void `apply()`
     957              : /// function that mutates the object. `Function::apply()` takes as its
     958              : /// first argument a `gsl::not_null` pointer to the object named by
     959              : /// the `GlobalCacheTag`, and takes `args` as
     960              : /// subsequent arguments.
     961              : template <typename GlobalCacheTag, typename Function, typename Metavariables,
     962              :           typename... Args>
     963            1 : void mutate(GlobalCache<Metavariables>& cache, Args&&... args) {
     964              :   if (cache.get_main_proxy().has_value()) {
     965              :     if constexpr (not GlobalCache<Metavariables>::is_mocked) {
     966              :       cache.thisProxy.template mutate<GlobalCacheTag, Function>(
     967              :           std::make_tuple<Args...>(std::forward<Args>(args)...));
     968              :     } else {
     969              :       ERROR(
     970              :           "Main proxy is set but global cache is being mocked. This is "
     971              :           "currently not implemented.");
     972              :     }
     973              :   } else {
     974              :     cache.template mutate<GlobalCacheTag, Function>(
     975              :         std::make_tuple<Args...>(std::forward<Args>(args)...));
     976              :   }
     977              : }
     978              : 
     979              : namespace Tags {
     980              : template <class Metavariables>
     981            0 : struct GlobalCacheProxy : db::SimpleTag {
     982            0 :   using type = CProxy_GlobalCache<Metavariables>;
     983              : };
     984              : 
     985              : /// \ingroup DataBoxTagsGroup
     986              : /// \ingroup ParallelGroup
     987              : /// Tag to retrieve the `Parallel::GlobalCache` from the DataBox.
     988              : template <class Metavariables>
     989            1 : struct GlobalCache : db::SimpleTag {
     990            0 :   using type = Parallel::GlobalCache<Metavariables>*;
     991              : };
     992              : 
     993              : template <class Metavariables>
     994            0 : struct GlobalCacheCompute : GlobalCache<Metavariables>, db::ComputeTag {
     995            0 :   using base = GlobalCache<Metavariables>;
     996            0 :   using argument_tags = tmpl::list<GlobalCacheProxy<Metavariables>>;
     997            0 :   using return_type = Parallel::GlobalCache<Metavariables>*;
     998            0 :   static void function(
     999              :       const gsl::not_null<Parallel::GlobalCache<Metavariables>**>
    1000              :           local_branch_of_global_cache,
    1001              :       const CProxy_GlobalCache<Metavariables>& global_cache_proxy) {
    1002              :     *local_branch_of_global_cache = Parallel::local_branch(global_cache_proxy);
    1003              :   }
    1004              : };
    1005              : 
    1006              : /// \ingroup DataBoxTagsGroup
    1007              : /// \ingroup ParallelGroup
    1008              : /// Tag used to retrieve data from the `Parallel::GlobalCache`. This is the
    1009              : /// recommended way for compute tags to retrieve data out of the global cache.
    1010              : template <class CacheTag, class Metavariables>
    1011            1 : struct FromGlobalCache : CacheTag, db::ReferenceTag {
    1012              :   static_assert(db::is_simple_tag_v<CacheTag>);
    1013            0 :   using base = CacheTag;
    1014            0 :   using argument_tags = tmpl::list<GlobalCache<Metavariables>>;
    1015              : 
    1016            0 :   static const auto& get(
    1017              :       const Parallel::GlobalCache<Metavariables>* const& cache) {
    1018              :     return Parallel::get<CacheTag>(*cache);
    1019              :   }
    1020              : };
    1021              : 
    1022              : template <typename Metavariables>
    1023            0 : struct ResourceInfoReference : ResourceInfo<Metavariables>, db::ReferenceTag {
    1024            0 :   using base = ResourceInfo<Metavariables>;
    1025            0 :   using argument_tags = tmpl::list<GlobalCache<Metavariables>>;
    1026              : 
    1027            0 :   static const auto& get(
    1028              :       const Parallel::GlobalCache<Metavariables>* const& cache) {
    1029              :     return cache->get_resource_info();
    1030              :   }
    1031              : };
    1032              : }  // namespace Tags
    1033              : }  // namespace Parallel
    1034              : 
    1035              : namespace PUP {
    1036              : /// \cond
    1037              : // Warning! This is an invalid and kludgey pupper, because when unpacking it
    1038              : // produces a `nullptr` rather than a valid `GlobalCache*`. This function is
    1039              : // provided _only_ to enable putting a `GlobalCache*` in the DataBox.
    1040              : //
    1041              : // SpECTRE parallel components with a `GlobalCache*` in their DataBox should
    1042              : // set this pointer using a compute item that calls `Parallel::local_branch` on
    1043              : // a stored proxy. When deserializing the DataBox, these components should call
    1044              : // `db:mutate<GlobalCacheProxy>(box)` to force the DataBox to update the
    1045              : // pointer from the new Charm++ proxy.
    1046              : //
    1047              : // We do not currently anticipate needing to (de)serialize a `GlobalCache*`
    1048              : // outside of a DataBox. But if this need arises, it will be necessary to
    1049              : // provide a non-kludgey pupper here.
    1050              : //
    1051              : // Correctly (de)serializing the `GlobalCache*` would require obtaining a
    1052              : // `CProxy_GlobalCache` item and calling `Parallel::local_branch` on it ---
    1053              : // just as in the workflow described above, but within the pupper vs in the
    1054              : // DataBox. But this strategy fails when restarting from a checkpoint file,
    1055              : // because calling `Parallel::local_branch` may not be well-defined in the
    1056              : // unpacking pup call when all Charm++ components may not yet be fully restored.
    1057              : // This difficulty is why we instead write an invalid pupper here.
    1058              : //
    1059              : // In future versions of Charm++, the pup function may know whether it is
    1060              : // called in the context of checkpointing, load balancing, etc. This knowledge
    1061              : // would enable us to write a valid pupper for non-checkpoint contexts, and
    1062              : // return a `nullptr` only when restoring from checkpoint.
    1063              : template <typename Metavariables>
    1064              : inline void operator|(PUP::er& p,  // NOLINT
    1065              :                       Parallel::GlobalCache<Metavariables>*& t) {
    1066              :   if (p.isUnpacking()) {
    1067              :     t = nullptr;
    1068              :   }
    1069              : }
    1070              : /// \endcond
    1071              : }  // namespace PUP
    1072              : 
    1073            0 : #define CK_TEMPLATES_ONLY
    1074              : #include "Parallel/GlobalCache.def.h"
    1075              : #undef CK_TEMPLATES_ONLY
        

Generated by: LCOV version 2.0-1