Line data Source code
1 0 : // Distributed under the MIT License. 2 : // See LICENSE.txt for details. 3 : 4 : #pragma once 5 : 6 : #include <memory> 7 : #include <optional> 8 : #include <pup.h> 9 : #include <string> 10 : #include <type_traits> 11 : 12 : #include "ControlSystem/CombinedName.hpp" 13 : #include "ControlSystem/FutureMeasurements.hpp" 14 : #include "ControlSystem/Metafunctions.hpp" 15 : #include "IO/Logging/Verbosity.hpp" 16 : #include "Parallel/ArrayComponentId.hpp" 17 : #include "Parallel/Callback.hpp" 18 : #include "Parallel/GlobalCache.hpp" 19 : #include "Parallel/Printf/Printf.hpp" 20 : #include "ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp" 21 : #include "Utilities/ErrorHandling/Assert.hpp" 22 : #include "Utilities/GetOutput.hpp" 23 : #include "Utilities/Gsl.hpp" 24 : #include "Utilities/PrettyType.hpp" 25 : #include "Utilities/Serialization/CharmPupable.hpp" 26 : #include "Utilities/TMPL.hpp" 27 : 28 : /// \cond 29 : namespace Tags { 30 : struct Time; 31 : } // namespace Tags 32 : namespace control_system::Tags { 33 : template <typename ControlSystems> 34 : struct FutureMeasurements; 35 : struct MeasurementTimescales; 36 : struct Verbosity; 37 : } // namespace control_system::Tags 38 : /// \endcond 39 : 40 : namespace control_system { 41 : /// \ingroup ControlSystemsGroup 42 : /// \ingroup EventsAndTriggersGroup 43 : /// Trigger for control system measurements. 44 : /// 45 : /// This trigger is only intended to be used with the 46 : /// `control_system::Event` event. A specialization of this trigger 47 : /// will be created during control system initialization for each 48 : /// unique \ref control_system::protocols::Measurement "measurement". 49 : /// 50 : /// These triggers must be added to the \ref 51 : /// Options::protocols::FactoryCreation "factory_creation" struct in 52 : /// the metavariables, even though they cannot be created from the 53 : /// input file. The `control_system::control_system_triggers` 54 : /// metafunction provides the list of triggers to include. 55 : template <typename ControlSystems> 56 1 : class Trigger : public DenseTrigger { 57 : static_assert(tmpl::size<ControlSystems>::value > 0); 58 0 : using measurement = typename tmpl::front<ControlSystems>::measurement; 59 : static_assert(tmpl::all<ControlSystems, 60 : std::is_same<metafunctions::measurement<tmpl::_1>, 61 : tmpl::pin<measurement>>>::value); 62 : 63 : public: 64 : /// \cond 65 : // LCOV_EXCL_START 66 : explicit Trigger(CkMigrateMessage* const msg) : DenseTrigger(msg) {} 67 : using PUP::able::register_constructor; 68 : WRAPPED_PUPable_decl_template(Trigger); // NOLINT 69 : // LCOV_EXCL_STOP 70 : /// \endcond 71 : 72 : // This trigger is created during control system initialization, not 73 : // from the input file. 74 0 : static constexpr bool factory_creatable = false; 75 0 : Trigger() = default; 76 : 77 0 : using is_triggered_return_tags = tmpl::list<>; 78 0 : using is_triggered_argument_tags = 79 : tmpl::list<::Tags::Time, 80 : control_system::Tags::FutureMeasurements<ControlSystems>>; 81 : 82 : template <typename Metavariables, typename ArrayIndex, typename Component> 83 0 : std::optional<bool> is_triggered( 84 : Parallel::GlobalCache<Metavariables>& cache, 85 : const ArrayIndex& array_index, const Component* /*component*/, 86 : const double time, 87 : const control_system::FutureMeasurements& measurement_times) { 88 : const auto next_measurement = measurement_times.next_measurement(); 89 : ASSERT(next_measurement.has_value(), 90 : "Checking trigger without knowing next time."); 91 : const bool triggered = time == *next_measurement; 92 : 93 : if (Parallel::get<Tags::Verbosity>(cache) >= ::Verbosity::Debug) { 94 : Parallel::printf( 95 : "%s, time = %.16f: Trigger for control systems (%s) is%s " 96 : "triggered.\n", 97 : get_output(array_index), time, 98 : pretty_type::list_of_names<ControlSystems>(), 99 : (triggered ? "" : " not")); 100 : } 101 : 102 : return triggered; 103 : } 104 : 105 0 : using next_check_time_return_tags = 106 : tmpl::list<control_system::Tags::FutureMeasurements<ControlSystems>>; 107 0 : using next_check_time_argument_tags = tmpl::list<::Tags::Time>; 108 : 109 : template <typename Metavariables, typename ArrayIndex, typename Component> 110 0 : std::optional<double> next_check_time( 111 : Parallel::GlobalCache<Metavariables>& cache, 112 : const ArrayIndex& array_index, const Component* /*component*/, 113 : const gsl::not_null<control_system::FutureMeasurements*> 114 : measurement_times, 115 : const double time) { 116 : if (measurement_times->next_measurement() == std::optional(time)) { 117 : measurement_times->pop_front(); 118 : } 119 : 120 : if (not measurement_times->next_measurement().has_value()) { 121 : const auto& proxy = 122 : ::Parallel::get_parallel_component<Component>(cache)[array_index]; 123 : const bool is_ready = Parallel::mutable_cache_item_is_ready< 124 : control_system::Tags::MeasurementTimescales>( 125 : cache, Parallel::make_array_component_id<Component>(array_index), 126 : [&](const auto& measurement_timescales) { 127 : const std::string& measurement_name = 128 : control_system::combined_name<ControlSystems>(); 129 : ASSERT(measurement_timescales.count(measurement_name) == 1, 130 : "Control system trigger expects a measurement timescale " 131 : "with the name '" 132 : << measurement_name 133 : << "' but could not find one. Available names are: " 134 : << keys_of(measurement_timescales)); 135 : measurement_times->update( 136 : *measurement_timescales.at(measurement_name)); 137 : if (not measurement_times->next_measurement().has_value()) { 138 : return std::unique_ptr<Parallel::Callback>( 139 : new Parallel::PerformAlgorithmCallback(proxy)); 140 : } 141 : return std::unique_ptr<Parallel::Callback>{}; 142 : }); 143 : 144 : if (not is_ready) { 145 : if (Parallel::get<Tags::Verbosity>(cache) >= ::Verbosity::Debug) { 146 : Parallel::printf( 147 : "%s, time = %.16f: Trigger for control systems (%s) - Cannot " 148 : "calculate next_check_time\n", 149 : get_output(array_index), time, 150 : pretty_type::list_of_names<ControlSystems>()); 151 : } 152 : return std::nullopt; 153 : } 154 : } 155 : 156 : const double next_trigger = *measurement_times->next_measurement(); 157 : ASSERT(next_trigger > time, 158 : "Next trigger is in the past: " << next_trigger << " > " << time); 159 : 160 : if (Parallel::get<Tags::Verbosity>(cache) >= ::Verbosity::Debug) { 161 : Parallel::printf( 162 : "%s, time = %.16f: Trigger for control systems (%s) - next check " 163 : "time is %.16f\n", 164 : get_output(array_index), time, 165 : pretty_type::list_of_names<ControlSystems>(), next_trigger); 166 : } 167 : 168 : return {next_trigger}; 169 : } 170 : }; 171 : 172 : /// \cond 173 : template <typename ControlSystems> 174 : PUP::able::PUP_ID Trigger<ControlSystems>::my_PUP_ID = 0; // NOLINT 175 : /// \endcond 176 : 177 : // This metafunction is tested in Test_EventTriggerMetafunctions.cpp 178 : 179 : /// \ingroup ControlSystemGroup 180 : /// The list of triggers needed for measurements for a list of control 181 : /// systems. 182 : template <typename ControlSystems> 183 1 : using control_system_triggers = tmpl::transform< 184 : metafunctions::measurements_t<ControlSystems>, 185 : tmpl::bind<Trigger, metafunctions::control_systems_with_measurement< 186 : tmpl::pin<ControlSystems>, tmpl::_1>>>; 187 : } // namespace control_system