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

Generated by: LCOV version 1.14