SpECTRE Documentation Coverage Report
Current view: top level - Parallel - GlobalCache.hpp Hit Total Coverage
Commit: 1f2210958b4f38fdc0400907ee7c6d5af5111418 Lines: 32 79 40.5 %
Date: 2025-12-05 05:03:31
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 "Parallel/ArrayComponentId.hpp"
      24             : #include "Parallel/Callback.hpp"
      25             : #include "Parallel/CharmRegistration.hpp"
      26             : #include "Parallel/Info.hpp"
      27             : #include "Parallel/Local.hpp"
      28             : #include "Parallel/ParallelComponentHelpers.hpp"
      29             : #include "Parallel/ResourceInfo.hpp"
      30             : #include "Parallel/Tags/ResourceInfo.hpp"
      31             : #include "Utilities/Algorithm.hpp"
      32             : #include "Utilities/ErrorHandling/Assert.hpp"
      33             : #include "Utilities/ErrorHandling/Error.hpp"
      34             : #include "Utilities/Gsl.hpp"
      35             : #include "Utilities/Numeric.hpp"
      36             : #include "Utilities/PrettyType.hpp"
      37             : #include "Utilities/Requires.hpp"
      38             : #include "Utilities/Serialization/PupStlCpp17.hpp"
      39             : #include "Utilities/Serialization/Serialize.hpp"
      40             : #include "Utilities/StdHelpers/RetrieveUniquePtr.hpp"
      41             : #include "Utilities/System/ParallelInfo.hpp"
      42             : #include "Utilities/TMPL.hpp"
      43             : #include "Utilities/TaggedTuple.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(const tuples::tagged_tuple_from_typelist<
     368             :                           Parallel::get_overlayable_option_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             :     const tuples::tagged_tuple_from_typelist<
     657             :         Parallel::get_overlayable_option_list<Metavariables>>&
     658             :         data_to_overlay) {
     659             :   // Entries in `get_overlayable_tag_list` and `get_overlayable_option_list`
     660             :   // have a 1-to-1 mapping: one is the option tag for the other. We loop
     661             :   // over the tag because it's easy to call tag::option_tag but not vice versa.
     662             :   tmpl::for_each<Parallel::get_overlayable_tag_list<Metavariables>>(
     663             :       [this, &data_to_overlay](auto tag) {
     664             :         using Tag = typename decltype(tag)::type;
     665             :         static_assert(tmpl::size<typename Tag::option_tags>::value == 1,
     666             :                       "The current implementation can only update tags "
     667             :                       "constructed from a single option tag.");
     668             :         using OptionTag = tmpl::front<typename Tag::option_tags>;
     669             :         tuples::get<Tag>(const_global_cache_) =
     670             :             Tag::create_from_options(tuples::get<OptionTag>(data_to_overlay));
     671             :       });
     672             : }
     673             : 
     674             : #if defined(__GNUC__) && !defined(__clang__)
     675             : #pragma GCC diagnostic push
     676             : #pragma GCC diagnostic ignored "-Wsuggest-attribute=noreturn"
     677             : #endif  // defined(__GNUC__) && !defined(__clang__)
     678             : template <typename Metavariables>
     679             : void GlobalCache<Metavariables>::compute_size_for_memory_monitor(
     680             :     const double time) {
     681             :   if constexpr (tmpl::list_contains_v<
     682             :                     typename Metavariables::component_list,
     683             :                     mem_monitor::MemoryMonitor<Metavariables>>) {
     684             :     const double size_in_bytes =
     685             :         static_cast<double>(size_of_object_in_bytes(*this));
     686             :     const double size_in_MB = size_in_bytes / 1.0e6;
     687             : 
     688             :     auto& mem_monitor_proxy = Parallel::get_parallel_component<
     689             :         mem_monitor::MemoryMonitor<Metavariables>>(*this);
     690             : 
     691             :     const int my_node = Parallel::my_node<int>(*this);
     692             : 
     693             :     Parallel::simple_action<
     694             :         mem_monitor::ContributeMemoryData<GlobalCache<Metavariables>>>(
     695             :         mem_monitor_proxy, time, my_node, size_in_MB);
     696             :   } else {
     697             :     (void)time;
     698             :     ERROR(
     699             :         "GlobalCache::compute_size_for_memory_monitor can only be called if "
     700             :         "the MemoryMonitor is in the component list in the metavariables.\n");
     701             :   }
     702             : }
     703             : 
     704             : template <typename Metavariables>
     705             : void GlobalCache<Metavariables>::set_resource_info(
     706             :     const Parallel::ResourceInfo<Metavariables>& resource_info) {
     707             :   ASSERT(not resource_info_has_been_set_,
     708             :          "Can only set the resource info once");
     709             :   resource_info_ = resource_info;
     710             :   resource_info_has_been_set_ = true;
     711             : }
     712             : 
     713             : template <typename Metavariables>
     714             : void GlobalCache<Metavariables>::print_mutable_cache_callbacks(
     715             :     const std::string& file_name) {
     716             :   std::stringstream ss{};
     717             :   ss << std::setprecision(std::numeric_limits<double>::digits10 + 4)
     718             :      << std::scientific;
     719             : 
     720             :   ss << "========== BEGIN CALLBACKS ON NODE " << this->my_node()
     721             :      << " ==========\n";
     722             : 
     723             :   tmpl::for_each<typename MutableTagsStorage::tags_list>(
     724             :       [this, &ss](auto tag_v) {
     725             :         using Tag = tmpl::type_from<decltype(tag_v)>;
     726             : 
     727             :         const auto& callbacks =
     728             :             std::get<1>(tuples::get<Tag>(mutable_global_cache_));
     729             : 
     730             :         ss << "For tag " << pretty_type::name<typename Tag::tag>()
     731             :            << ", callbacks ("
     732             :            << alg::accumulate(callbacks, 0_st,
     733             :                               [](const size_t cur_size, const auto& v) {
     734             :                                 return cur_size + v.second.size();
     735             :                               })
     736             :            << "):\n";
     737             : 
     738             :         for (const auto& [array_component_id, vec_callbacks] : callbacks) {
     739             :           for (const auto& callback : vec_callbacks) {
     740             :             ss << "  ArrayComponentId " << array_component_id << ": "
     741             :                << callback->name() << "\n";
     742             :           }
     743             :         }
     744             :       });
     745             : 
     746             :   ss << "========== END CALLBACKS ON NODE " << this->my_node()
     747             :      << " ============\n";
     748             : 
     749             :   Parallel::fprintf(file_name, "%s\n", ss.str());
     750             : }
     751             : 
     752             : #if defined(__GNUC__) && !defined(__clang__)
     753             : #pragma GCC diagnostic pop
     754             : #endif  // defined(__GNUC__) && !defined(__clang__)
     755             : 
     756             : template <typename Metavariables>
     757             : typename Parallel::GlobalCache<Metavariables>::proxy_type
     758             : GlobalCache<Metavariables>::get_this_proxy() {
     759             :   if constexpr (is_mocked) {
     760             :     // The proxy is not used in the testing framework
     761             :     return Parallel::GlobalCache<Metavariables>::proxy_type{};
     762             :   } else {
     763             :     return this->thisProxy;
     764             :   }
     765             : }
     766             : 
     767             : template <typename Metavariables>
     768             : std::optional<typename Parallel::GlobalCache<Metavariables>::main_proxy_type>
     769             : GlobalCache<Metavariables>::get_main_proxy() {
     770             :   return main_proxy_;
     771             : }
     772             : 
     773             : // For all these functions, if the main proxy is set (meaning we are
     774             : // charm-aware) then just call the sys:: functions. Otherwise, use the values
     775             : // set for the testing framework (or the defaults).
     776             : template <typename Metavariables>
     777             : int GlobalCache<Metavariables>::number_of_procs() const {
     778             :   return main_proxy_.has_value()
     779             :              ? sys::number_of_procs()
     780             :              : static_cast<int>(alg::accumulate(procs_per_node_, 0_st));
     781             : }
     782             : 
     783             : template <typename Metavariables>
     784             : int GlobalCache<Metavariables>::number_of_nodes() const {
     785             :   return main_proxy_.has_value() ? sys::number_of_nodes()
     786             :                                  : static_cast<int>(procs_per_node_.size());
     787             : }
     788             : 
     789             : template <typename Metavariables>
     790             : int GlobalCache<Metavariables>::procs_on_node(const int node_index) const {
     791             :   return main_proxy_.has_value()
     792             :              ? sys::procs_on_node(node_index)
     793             :              : static_cast<int>(
     794             :                    procs_per_node_[static_cast<size_t>(node_index)]);
     795             : }
     796             : 
     797             : template <typename Metavariables>
     798             : int GlobalCache<Metavariables>::first_proc_on_node(const int node_index) const {
     799             :   return main_proxy_.has_value()
     800             :              ? sys::first_proc_on_node(node_index)
     801             :              : static_cast<int>(
     802             :                    std::accumulate(procs_per_node_.begin(),
     803             :                                    procs_per_node_.begin() + node_index, 0_st));
     804             : }
     805             : 
     806             : template <typename Metavariables>
     807             : int GlobalCache<Metavariables>::node_of(const int proc_index) const {
     808             :   if (main_proxy_.has_value()) {
     809             :     // For some reason gcov doesn't think this line is tested even though it is
     810             :     // in Test_AlgorithmGlobalCache.cpp
     811             :     return sys::node_of(proc_index);  // LCOV_EXCL_LINE
     812             :   } else {
     813             :     size_t procs_so_far = 0;
     814             :     size_t node = 0;
     815             :     while (procs_so_far <= static_cast<size_t>(proc_index)) {
     816             :       procs_so_far += procs_per_node_[node];
     817             :       ++node;
     818             :     }
     819             :     return static_cast<int>(--node);
     820             :   }
     821             : }
     822             : 
     823             : template <typename Metavariables>
     824             : int GlobalCache<Metavariables>::local_rank_of(const int proc_index) const {
     825             :   return main_proxy_.has_value()
     826             :              ? sys::local_rank_of(proc_index)
     827             :              : proc_index - first_proc_on_node(node_of(proc_index));
     828             : }
     829             : 
     830             : template <typename Metavariables>
     831             : int GlobalCache<Metavariables>::my_proc() const {
     832             :   return main_proxy_.has_value() ? sys::my_proc() : my_proc_;
     833             : }
     834             : 
     835             : template <typename Metavariables>
     836             : int GlobalCache<Metavariables>::my_node() const {
     837             :   return main_proxy_.has_value() ? sys::my_node() : my_node_;
     838             : }
     839             : 
     840             : template <typename Metavariables>
     841             : int GlobalCache<Metavariables>::my_local_rank() const {
     842             :   return main_proxy_.has_value() ? sys::my_local_rank() : my_local_rank_;
     843             : }
     844             : 
     845             : template <typename Metavariables>
     846             : void GlobalCache<Metavariables>::pup(PUP::er& p) {
     847             :   p | const_global_cache_;
     848             :   p | parallel_components_;
     849             :   p | mutable_global_cache_;
     850             :   p | main_proxy_;
     851             :   p | parallel_components_have_been_set_;
     852             :   p | resource_info_has_been_set_;
     853             :   p | my_proc_;
     854             :   p | my_node_;
     855             :   p | my_local_rank_;
     856             :   p | procs_per_node_;
     857             : }
     858             : 
     859             : /// @{
     860             : /// \ingroup ParallelGroup
     861             : /// \brief Access the Charm++ proxy associated with a ParallelComponent
     862             : ///
     863             : /// \requires ParallelComponentTag is a tag in component_list
     864             : ///
     865             : /// \returns a Charm++ proxy that can be used to call an entry method on the
     866             : /// chare(s)
     867             : template <typename ParallelComponentTag, typename Metavariables>
     868           1 : auto get_parallel_component(GlobalCache<Metavariables>& cache)
     869             :     -> Parallel::proxy_from_parallel_component<
     870             :         GlobalCache_detail::get_component_if_mocked<
     871             :             typename Metavariables::component_list, ParallelComponentTag>>& {
     872             :   return tuples::get<tmpl::type_<Parallel::proxy_from_parallel_component<
     873             :       GlobalCache_detail::get_component_if_mocked<
     874             :           typename Metavariables::component_list, ParallelComponentTag>>>>(
     875             :       cache.parallel_components_);
     876             : }
     877             : 
     878             : template <typename ParallelComponentTag, typename Metavariables>
     879           1 : auto get_parallel_component(const GlobalCache<Metavariables>& cache)
     880             :     -> const Parallel::proxy_from_parallel_component<
     881             :         GlobalCache_detail::get_component_if_mocked<
     882             :             typename Metavariables::component_list, ParallelComponentTag>>& {
     883             :   return tuples::get<tmpl::type_<Parallel::proxy_from_parallel_component<
     884             :       GlobalCache_detail::get_component_if_mocked<
     885             :           typename Metavariables::component_list, ParallelComponentTag>>>>(
     886             :       cache.parallel_components_);
     887             : }
     888             : /// @}
     889             : 
     890             : /// @{
     891             : /// \ingroup ParallelGroup
     892             : /// \brief Access data in the cache
     893             : ///
     894             : /// \requires GlobalCacheTag is a tag in the `mutable_global_cache_tags`
     895             : /// or `const_global_cache_tags` defined by the Metavariables and in Actions.
     896             : ///
     897             : /// \returns a constant reference to an object in the cache
     898             : template <typename GlobalCacheTag, typename Metavariables>
     899           1 : auto get(const GlobalCache<Metavariables>& cache)
     900             :     -> const GlobalCache_detail::type_for_get<GlobalCacheTag, Metavariables>& {
     901             :   constexpr bool is_mutable =
     902             :       is_in_mutable_global_cache<Metavariables, GlobalCacheTag>;
     903             :   // We check if the tag is to be retrieved directly or via a base class
     904             :   using tmp_tag =
     905             :       GlobalCache_detail::get_matching_tag<GlobalCacheTag, Metavariables>;
     906             :   using tag =
     907             :       tmpl::conditional_t<is_mutable, MutableCacheTag<tmp_tag>, tmp_tag>;
     908             :   if constexpr (is_mutable) {
     909             :     // Tag is not in the const tags, so use mutable_global_cache_. No locks here
     910             :     // because we require all mutable tags to be able to be read at all times
     911             :     // (even when being written to)
     912             :     if constexpr (tt::is_a_v<std::unique_ptr, typename tag::tag::type>) {
     913             :       return *std::get<0>(tuples::get<tag>(cache.mutable_global_cache_));
     914             :     } else {
     915             :       return std::get<0>(tuples::get<tag>(cache.mutable_global_cache_));
     916             :     }
     917             :   } else {
     918             :     // Tag is in the const tags, so use const_global_cache_
     919             :     if constexpr (tt::is_a_v<std::unique_ptr, typename tag::type>) {
     920             :       return *(tuples::get<tag>(cache.const_global_cache_));
     921             :     } else {
     922             :       return tuples::get<tag>(cache.const_global_cache_);
     923             :     }
     924             :   }
     925             : }
     926             : 
     927             : /// \ingroup ParallelGroup
     928             : /// \brief Returns whether the object identified by `GlobalCacheTag`
     929             : /// is ready to be accessed by `get`.
     930             : ///
     931             : /// \requires `GlobalCacheTag` is a tag in `mutable_global_cache_tags`
     932             : /// defined by the Metavariables and in Actions.
     933             : ///
     934             : /// \requires `function` is a user-defined invokable that takes one argument:
     935             : /// a const reference to the object referred to by the
     936             : /// `GlobalCacheTag`.  `function` returns a
     937             : /// `std::unique_ptr<CallBack>` that determines the readiness. To
     938             : /// indicate that the item is ready, the `std::unique_ptr` returned
     939             : /// by `function` must be nullptr; in this case
     940             : /// `mutable_cache_item_is_ready` returns true. To indicate that the
     941             : /// item is not ready, the `std::unique_ptr` returned by `function`
     942             : /// must be valid; in this case, `mutable_cache_item_is_ready`
     943             : /// appends the `std::unique_ptr<Callback>` to an
     944             : /// internal list of callbacks to be called on `mutate`, and then
     945             : /// returns false.
     946             : ///
     947             : /// \note If `function` is returning a valid callback, it should only return a
     948             : /// `Parallel::PerformAlgorithmCallback`. Other types of callbacks are not
     949             : /// supported at this time.
     950             : template <typename GlobalCacheTag, typename Function, typename Metavariables>
     951           1 : bool mutable_cache_item_is_ready(
     952             :     GlobalCache<Metavariables>& cache,
     953             :     const Parallel::ArrayComponentId& array_component_id,
     954             :     const Function& function) {
     955             :   return cache.template mutable_cache_item_is_ready<GlobalCacheTag>(
     956             :       array_component_id, function);
     957             : }
     958             : 
     959             : /// \ingroup ParallelGroup
     960             : ///
     961             : /// \brief Mutates non-const data in the cache, by calling `Function::apply()`
     962             : ///
     963             : /// \requires `GlobalCacheTag` is a tag in tag_list.
     964             : /// \requires `Function` is a struct with a static void `apply()`
     965             : /// function that mutates the object. `Function::apply()` takes as its
     966             : /// first argument a `gsl::not_null` pointer to the object named by
     967             : /// the `GlobalCacheTag`, and takes `args` as
     968             : /// subsequent arguments.
     969             : template <typename GlobalCacheTag, typename Function, typename Metavariables,
     970             :           typename... Args>
     971           1 : void mutate(GlobalCache<Metavariables>& cache, Args&&... args) {
     972             :   if (cache.get_main_proxy().has_value()) {
     973             :     if constexpr (not GlobalCache<Metavariables>::is_mocked) {
     974             :       cache.thisProxy.template mutate<GlobalCacheTag, Function>(
     975             :           std::make_tuple<Args...>(std::forward<Args>(args)...));
     976             :     } else {
     977             :       ERROR(
     978             :           "Main proxy is set but global cache is being mocked. This is "
     979             :           "currently not implemented.");
     980             :     }
     981             :   } else {
     982             :     cache.template mutate<GlobalCacheTag, Function>(
     983             :         std::make_tuple<Args...>(std::forward<Args>(args)...));
     984             :   }
     985             : }
     986             : 
     987             : namespace Tags {
     988             : template <class Metavariables>
     989           0 : struct GlobalCacheProxy : db::SimpleTag {
     990           0 :   using type = CProxy_GlobalCache<Metavariables>;
     991             : };
     992             : 
     993             : /// \ingroup DataBoxTagsGroup
     994             : /// \ingroup ParallelGroup
     995             : /// Tag to retrieve the `Parallel::GlobalCache` from the DataBox.
     996             : template <class Metavariables>
     997           1 : struct GlobalCache : db::SimpleTag {
     998           0 :   using type = Parallel::GlobalCache<Metavariables>*;
     999             : };
    1000             : 
    1001             : template <class Metavariables>
    1002           0 : struct GlobalCacheCompute : GlobalCache<Metavariables>, db::ComputeTag {
    1003           0 :   using base = GlobalCache<Metavariables>;
    1004           0 :   using argument_tags = tmpl::list<GlobalCacheProxy<Metavariables>>;
    1005           0 :   using return_type = Parallel::GlobalCache<Metavariables>*;
    1006           0 :   static void function(
    1007             :       const gsl::not_null<Parallel::GlobalCache<Metavariables>**>
    1008             :           local_branch_of_global_cache,
    1009             :       const CProxy_GlobalCache<Metavariables>& global_cache_proxy) {
    1010             :     *local_branch_of_global_cache = Parallel::local_branch(global_cache_proxy);
    1011             :   }
    1012             : };
    1013             : 
    1014             : /// \ingroup DataBoxTagsGroup
    1015             : /// \ingroup ParallelGroup
    1016             : /// Tag used to retrieve data from the `Parallel::GlobalCache`. This is the
    1017             : /// recommended way for compute tags to retrieve data out of the global cache.
    1018             : template <class CacheTag, class Metavariables>
    1019           1 : struct FromGlobalCache : CacheTag, db::ReferenceTag {
    1020             :   static_assert(db::is_simple_tag_v<CacheTag>);
    1021           0 :   using base = CacheTag;
    1022           0 :   using argument_tags = tmpl::list<GlobalCache<Metavariables>>;
    1023             : 
    1024           0 :   static const auto& get(
    1025             :       const Parallel::GlobalCache<Metavariables>* const& cache) {
    1026             :     return Parallel::get<CacheTag>(*cache);
    1027             :   }
    1028             : };
    1029             : 
    1030             : template <typename Metavariables>
    1031           0 : struct ResourceInfoReference : ResourceInfo<Metavariables>, db::ReferenceTag {
    1032           0 :   using base = ResourceInfo<Metavariables>;
    1033           0 :   using argument_tags = tmpl::list<GlobalCache<Metavariables>>;
    1034             : 
    1035           0 :   static const auto& get(
    1036             :       const Parallel::GlobalCache<Metavariables>* const& cache) {
    1037             :     return cache->get_resource_info();
    1038             :   }
    1039             : };
    1040             : }  // namespace Tags
    1041             : }  // namespace Parallel
    1042             : 
    1043             : namespace PUP {
    1044             : /// \cond
    1045             : // Warning! This is an invalid and kludgey pupper, because when unpacking it
    1046             : // produces a `nullptr` rather than a valid `GlobalCache*`. This function is
    1047             : // provided _only_ to enable putting a `GlobalCache*` in the DataBox.
    1048             : //
    1049             : // SpECTRE parallel components with a `GlobalCache*` in their DataBox should
    1050             : // set this pointer using a compute item that calls `Parallel::local_branch` on
    1051             : // a stored proxy. When deserializing the DataBox, these components should call
    1052             : // `db:mutate<GlobalCacheProxy>(box)` to force the DataBox to update the
    1053             : // pointer from the new Charm++ proxy.
    1054             : //
    1055             : // We do not currently anticipate needing to (de)serialize a `GlobalCache*`
    1056             : // outside of a DataBox. But if this need arises, it will be necessary to
    1057             : // provide a non-kludgey pupper here.
    1058             : //
    1059             : // Correctly (de)serializing the `GlobalCache*` would require obtaining a
    1060             : // `CProxy_GlobalCache` item and calling `Parallel::local_branch` on it ---
    1061             : // just as in the workflow described above, but within the pupper vs in the
    1062             : // DataBox. But this strategy fails when restarting from a checkpoint file,
    1063             : // because calling `Parallel::local_branch` may not be well-defined in the
    1064             : // unpacking pup call when all Charm++ components may not yet be fully restored.
    1065             : // This difficulty is why we instead write an invalid pupper here.
    1066             : //
    1067             : // In future versions of Charm++, the pup function may know whether it is
    1068             : // called in the context of checkpointing, load balancing, etc. This knowledge
    1069             : // would enable us to write a valid pupper for non-checkpoint contexts, and
    1070             : // return a `nullptr` only when restoring from checkpoint.
    1071             : template <typename Metavariables>
    1072             : inline void operator|(PUP::er& p,  // NOLINT
    1073             :                       Parallel::GlobalCache<Metavariables>*& t) {
    1074             :   if (p.isUnpacking()) {
    1075             :     t = nullptr;
    1076             :   }
    1077             : }
    1078             : /// \endcond
    1079             : }  // namespace PUP
    1080             : 
    1081           0 : #define CK_TEMPLATES_ONLY
    1082             : #include "Parallel/GlobalCache.def.h"
    1083             : #undef CK_TEMPLATES_ONLY

Generated by: LCOV version 1.14