text: // Distributed under the MIT License.
  // See LICENSE.txt for details.
  
  #pragma once
  
  #include <cstddef>
  #include <tuple>
  
  #include "DataStructures/DataBox/DataBox.hpp"
  #include "Evolution/EventsAndDenseTriggers/EventsAndDenseTriggers.hpp"
  #include "Evolution/EventsAndDenseTriggers/Tags.hpp"
  #include "Parallel/AlgorithmMetafunctions.hpp"
  #include "Time/EvolutionOrdering.hpp"
  #include "Time/Tags.hpp"
  #include "Time/TimeSteppers/TimeStepper.hpp"
  #include "Utilities/Gsl.hpp"
  #include "Utilities/TMPL.hpp"
  #include "Utilities/TaggedTuple.hpp"
  
  /// \cond
  namespace Parallel {
  template <typename Metavariables>
  class GlobalCache;
  }  // namespace Parallel
  namespace Tags {
  struct TimeStep;
  }  // namespace Tags
  /// \endcond
  
  namespace evolution::Actions {
  /// \ingroup ActionsGroup
  /// \ingroup EventsAndTriggersGroup
  /// \brief Run the events and dense triggers
  ///
  /// Uses:
  /// - DataBox: EventsAndDenseTriggers, as required by events and triggers
  ///
  /// DataBox changes:
  /// - Adds: nothing
  /// - Removes: nothing
  /// - Modifies: nothing
  template <typename PrimFromCon = void>
  struct RunEventsAndDenseTriggers {
    template <typename DbTags, typename... InboxTags, typename Metavariables,
              typename ArrayIndex, typename ActionList,
              typename ParallelComponent>
    static std::tuple<db::DataBox<DbTags>&&, Parallel::AlgorithmExecution> apply(
        db::DataBox<DbTags>& box,
        tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
        Parallel::GlobalCache<Metavariables>& cache,
        const ArrayIndex& array_index, const ActionList /*meta*/,
        const ParallelComponent* const component) noexcept {
      const auto& time_step_id = db::get<::Tags::TimeStepId>(box);
      if (time_step_id.slab_number() < 0) {
        // Skip dense output during self-start
        return {std::move(box), Parallel::AlgorithmExecution::Continue};
      }
  
      auto& events_and_dense_triggers =
          db::get_mutable_reference<::evolution::Tags::EventsAndDenseTriggers>(
              make_not_null(&box));
  
      const auto step_end =
          time_step_id.step_time() + db::get<::Tags::TimeStep>(box);
      const evolution_less<double> before{time_step_id.time_runs_forward()};
  
      for (;;) {
        const double next_trigger = events_and_dense_triggers.next_trigger(box);
        if (before(step_end.value(), next_trigger)) {
          return {std::move(box), Parallel::AlgorithmExecution::Continue};
        }
  
        if (db::get<::Tags::Time>(box) != next_trigger) {
          db::mutate<::Tags::Time>(
              make_not_null(&box),
              [&next_trigger](const gsl::not_null<double*> time) noexcept {
                *time = next_trigger;
              });
        }
  
        const auto triggered = events_and_dense_triggers.is_ready(
            box, cache, array_index, component);
        using TriggeringState = std::decay_t<decltype(triggered)>;
        switch (triggered) {
          case TriggeringState::NotReady:
            return {std::move(box), Parallel::AlgorithmExecution::Retry};
          case TriggeringState::NeedsEvolvedVariables:
            {
              using variables_tag = typename Metavariables::system::variables_tag;
              using history_tag = ::Tags::HistoryEvolvedVariables<variables_tag>;
              bool dense_output_succeeded = false;
              db::mutate<variables_tag>(
                  make_not_null(&box),
                  [&dense_output_succeeded, &next_trigger](
                      gsl::not_null<typename variables_tag::type*> vars,
                      const TimeStepper& stepper,
                      const typename history_tag::type& history) noexcept {
                    dense_output_succeeded =
                        stepper.dense_update_u(vars, history, next_trigger);
                  },
                  db::get<::Tags::TimeStepper<>>(box), db::get<history_tag>(box));
              if (not dense_output_succeeded) {
                // With LTS, we may need to wait for neighbor data
                // rather than take another step.
                static_assert(not Metavariables::local_time_stepping,
                              "LTS support for RunDenseEventsAndTriggers is not "
                              "implemented.");
  
                // Need to take another time step
                return {std::move(box), Parallel::AlgorithmExecution::Continue};
              }
  
              static_assert(
                  Metavariables::system::has_primitive_and_conservative_vars !=
                      std::is_same_v<PrimFromCon, void>,
                  "Primitive update scheme not provided.");
              if constexpr (
                  Metavariables::system::has_primitive_and_conservative_vars) {
                db::mutate_apply<PrimFromCon>(make_not_null(&box));
              }
            }
            [[fallthrough]];
          default:
            break;
        }
  
        events_and_dense_triggers.run_events(box, cache, array_index, component);
      }
    }
  };
  
  struct InitializeRunEventsAndDenseTriggers {
    using initialization_tags =
        tmpl::list<evolution::Tags::EventsAndDenseTriggers>;
    using initialization_tags_to_keep = initialization_tags;
  
    template <typename DbTags, typename... InboxTags, typename Metavariables,
              typename ArrayIndex, typename ActionList,
              typename ParallelComponent>
    static auto apply(db::DataBox<DbTags>& box,
                      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
                      Parallel::GlobalCache<Metavariables>& /*cache*/,
                      const ArrayIndex& /*array_index*/,
                      const ActionList /*meta*/,
                      const ParallelComponent* const /*component*/) noexcept {
      return std::forward_as_tuple(std::move(box));
    }
  };
  }  // namespace evolution::Actions
  