Line data Source code
1 0 : // Distributed under the MIT License. 2 : // See LICENSE.txt for details. 3 : 4 : #pragma once 5 : 6 : #include <limits> 7 : #include <optional> 8 : #include <pup.h> 9 : #include <type_traits> 10 : 11 : #include "DataStructures/DataBox/DataBox.hpp" 12 : #include "Parallel/Tags/Metavariables.hpp" 13 : #include "Utilities/CallWithDynamicType.hpp" 14 : #include "Utilities/Gsl.hpp" 15 : #include "Utilities/Serialization/CharmPupable.hpp" 16 : #include "Utilities/Serialization/PupStlCpp17.hpp" 17 : 18 : /// \cond 19 : namespace Parallel { 20 : template <typename Metavariables> 21 : class GlobalCache; 22 : } // namespace Parallel 23 : namespace Tags { 24 : struct Time; 25 : } // namespace Tags 26 : /// \endcond 27 : 28 : /*! 29 : * \ingroup EventsAndTriggersGroup 30 : * \brief Contains denser triggers. 31 : */ 32 1 : namespace DenseTriggers {} 33 : 34 : /// \ingroup EventsAndTriggersGroup 35 : /// Base class for checking whether to run an Event at arbitrary times. 36 : /// 37 : /// The DataBox passed to the member functions will have 38 : /// `::Tags::Time`, and therefore any compute tags depending on that 39 : /// value, set to the time to be tested. Any discrete properties of 40 : /// steps or slabs, such as the step size, may have the values from 41 : /// times off by one step. The evolved variables will be in an 42 : /// unspecified state. 43 1 : class DenseTrigger : public PUP::able { 44 : protected: 45 : /// \cond 46 : DenseTrigger() = default; 47 : DenseTrigger(const DenseTrigger&) = default; 48 : DenseTrigger(DenseTrigger&&) = default; 49 : DenseTrigger& operator=(const DenseTrigger&) = default; 50 : DenseTrigger& operator=(DenseTrigger&&) = default; 51 : /// \endcond 52 : 53 : public: 54 0 : ~DenseTrigger() override = default; 55 : 56 : /// \cond 57 : explicit DenseTrigger(CkMigrateMessage* const msg) : PUP::able(msg) {} 58 : WRAPPED_PUPable_abstract(DenseTrigger); // NOLINT 59 : /// \endcond 60 : 61 : /// Check whether the trigger fires. Returns std::nullopt if 62 : /// insufficient data is available to make the decision. The 63 : /// trigger is not responsible for checking whether dense output of 64 : /// the evolved variables is possible, but may need to check things 65 : /// such as the availability of FunctionOfTime data. 66 : template <typename DbTags, typename Metavariables, typename ArrayIndex, 67 : typename Component> 68 1 : std::optional<bool> is_triggered( 69 : const gsl::not_null<db::DataBox<DbTags>*> box, 70 : Parallel::GlobalCache<Metavariables>& cache, 71 : const ArrayIndex& array_index, const Component* const component) { 72 : using factory_classes = 73 : typename std::decay_t<decltype(db::get<Parallel::Tags::Metavariables>( 74 : *box))>::factory_creation::factory_classes; 75 : previous_trigger_time_ = next_previous_trigger_time_; 76 : return call_with_dynamic_type<std::optional<bool>, 77 : tmpl::at<factory_classes, DenseTrigger>>( 78 : this, 79 : [this, &array_index, &box, &cache, &component](auto* const trigger) { 80 : using TriggerType = std::decay_t<decltype(*trigger)>; 81 : const auto result = db::mutate_apply< 82 : typename TriggerType::is_triggered_return_tags, 83 : typename TriggerType::is_triggered_argument_tags>( 84 : [&array_index, &cache, &component, 85 : &trigger](const auto&... args) { 86 : return trigger->is_triggered(cache, array_index, component, 87 : args...); 88 : }, 89 : box); 90 : if (result == std::optional{true}) { 91 : next_previous_trigger_time_ = db::get<::Tags::Time>(*box); 92 : } 93 : return result; 94 : }); 95 : } 96 : 97 : /// Obtain the next time to check the trigger, or std::nullopt if the 98 : /// trigger is not ready to report yet. 99 : template <typename DbTags, typename Metavariables, typename ArrayIndex, 100 : typename Component> 101 1 : std::optional<double> next_check_time( 102 : const gsl::not_null<db::DataBox<DbTags>*> box, 103 : Parallel::GlobalCache<Metavariables>& cache, 104 : const ArrayIndex& array_index, const Component* component) { 105 : using factory_classes = 106 : typename std::decay_t<decltype(db::get<Parallel::Tags::Metavariables>( 107 : *box))>::factory_creation::factory_classes; 108 : return call_with_dynamic_type<std::optional<double>, 109 : tmpl::at<factory_classes, DenseTrigger>>( 110 : this, [&array_index, &box, &cache, &component](auto* const trigger) { 111 : using TriggerType = std::decay_t<decltype(*trigger)>; 112 : return db::mutate_apply< 113 : typename TriggerType::next_check_time_return_tags, 114 : typename TriggerType::next_check_time_argument_tags>( 115 : [&array_index, &cache, &component, 116 : &trigger](const auto&... args) { 117 : return trigger->next_check_time(cache, array_index, component, 118 : args...); 119 : }, 120 : box); 121 : }); 122 : } 123 : 124 : /// \brief Reports the value of `::Tags::Time` when the trigger most recently 125 : /// fired, except for the most recent call of `is_triggered`. 126 : /// 127 : /// \details The most recent call of `is_triggered` is not used for reporting 128 : /// the previous trigger so that the time reported to the event is actually 129 : /// the previous time value on which the trigger fired and activated the 130 : /// event. Without ignoring the most recent call of `is_triggered`, we'd just 131 : /// always be reporting the current time to the event, because events always 132 : /// run after their associated triggers fire via a call to `is_triggered`. 133 1 : std::optional<double> previous_trigger_time() const { 134 : return previous_trigger_time_; 135 : } 136 : 137 0 : void pup(PUP::er& p) override { 138 : p | next_previous_trigger_time_; 139 : p | previous_trigger_time_; 140 : } 141 : 142 : private: 143 0 : std::optional<double> next_previous_trigger_time_ = std::nullopt; 144 0 : std::optional<double> previous_trigger_time_ = std::nullopt; 145 : };