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