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/ArrayCollection/IsDgElementCollection.hpp" 17 : #include "Parallel/ArrayCollection/PerformAlgorithmOnElement.hpp" 18 : #include "Parallel/ArrayCollection/Tags/ElementLocations.hpp" 19 : #include "Parallel/ArrayComponentId.hpp" 20 : #include "Parallel/Callback.hpp" 21 : #include "Parallel/GlobalCache.hpp" 22 : #include "Parallel/Printf/Printf.hpp" 23 : #include "ParallelAlgorithms/Actions/GetItemFromDistributedObject.hpp" 24 : #include "ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp" 25 : #include "Utilities/ErrorHandling/Assert.hpp" 26 : #include "Utilities/GetOutput.hpp" 27 : #include "Utilities/Gsl.hpp" 28 : #include "Utilities/PrettyType.hpp" 29 : #include "Utilities/Serialization/CharmPupable.hpp" 30 : #include "Utilities/TMPL.hpp" 31 : 32 : /// \cond 33 : namespace Tags { 34 : struct Time; 35 : } // namespace Tags 36 : namespace control_system::Tags { 37 : template <typename ControlSystems> 38 : struct FutureMeasurements; 39 : struct MeasurementTimescales; 40 : struct Verbosity; 41 : } // namespace control_system::Tags 42 : /// \endcond 43 : 44 : namespace control_system { 45 : /// \ingroup ControlSystemGroup 46 : /// \ingroup EventsAndTriggersGroup 47 : /// Trigger for control system measurements. 48 : /// 49 : /// This trigger is only intended to be used with the 50 : /// `control_system::Event` event. A specialization of this trigger 51 : /// will be created during control system initialization for each 52 : /// unique \ref control_system::protocols::Measurement "measurement". 53 : /// 54 : /// These triggers must be added to the \ref 55 : /// Options::protocols::FactoryCreation "factory_creation" struct in 56 : /// the metavariables, even though they cannot be created from the 57 : /// input file. The `control_system::control_system_triggers` 58 : /// metafunction provides the list of triggers to include. 59 : template <typename ControlSystems> 60 1 : class Trigger : public DenseTrigger { 61 : static_assert(tmpl::size<ControlSystems>::value > 0); 62 0 : using measurement = typename tmpl::front<ControlSystems>::measurement; 63 : static_assert(tmpl::all<ControlSystems, 64 : std::is_same<metafunctions::measurement<tmpl::_1>, 65 : tmpl::pin<measurement>>>::value); 66 : 67 : public: 68 : /// \cond 69 : // LCOV_EXCL_START 70 : explicit Trigger(CkMigrateMessage* const msg) : DenseTrigger(msg) {} 71 : using PUP::able::register_constructor; 72 : WRAPPED_PUPable_decl_template(Trigger); // NOLINT 73 : // LCOV_EXCL_STOP 74 : /// \endcond 75 : 76 : // This trigger is created during control system initialization, not 77 : // from the input file. 78 0 : static constexpr bool factory_creatable = false; 79 0 : Trigger() = default; 80 : 81 0 : using is_triggered_return_tags = tmpl::list<>; 82 0 : using is_triggered_argument_tags = 83 : tmpl::list<::Tags::Time, 84 : control_system::Tags::FutureMeasurements<ControlSystems>>; 85 : 86 : template <typename Metavariables, size_t Dim, typename Component> 87 0 : std::optional<bool> is_triggered( 88 : Parallel::GlobalCache<Metavariables>& cache, 89 : const ElementId<Dim>& array_index, const Component* /*component*/, 90 : const double time, 91 : const control_system::FutureMeasurements& measurement_times) { 92 : const auto next_measurement = measurement_times.next_measurement(); 93 : ASSERT(next_measurement.has_value(), 94 : "Checking trigger without knowing next time."); 95 : const bool triggered = time == *next_measurement; 96 : 97 : if (Parallel::get<Tags::Verbosity>(cache) >= ::Verbosity::Debug) { 98 : Parallel::printf( 99 : "%s, time = %.16f: Trigger for control systems (%s) is%s " 100 : "triggered.\n", 101 : get_output(array_index), time, 102 : pretty_type::list_of_names<ControlSystems>(), 103 : (triggered ? "" : " not")); 104 : } 105 : 106 : return triggered; 107 : } 108 : 109 0 : using next_check_time_return_tags = 110 : tmpl::list<control_system::Tags::FutureMeasurements<ControlSystems>>; 111 0 : using next_check_time_argument_tags = tmpl::list<::Tags::Time>; 112 : 113 : template <typename Metavariables, size_t Dim, typename Component> 114 0 : std::optional<double> next_check_time( 115 : Parallel::GlobalCache<Metavariables>& cache, 116 : const ElementId<Dim>& array_index, const Component* /*component*/, 117 : const gsl::not_null<control_system::FutureMeasurements*> 118 : measurement_times, 119 : const double time) { 120 : if (measurement_times->next_measurement() == std::optional(time)) { 121 : measurement_times->pop_front(); 122 : } 123 : 124 : if (not measurement_times->next_measurement().has_value()) { 125 : auto& proxy = ::Parallel::get_parallel_component<Component>(cache); 126 : const bool is_ready = Parallel::mutable_cache_item_is_ready< 127 : control_system::Tags::MeasurementTimescales>( 128 : cache, Parallel::make_array_component_id<Component>(array_index), 129 : [&](const auto& measurement_timescales) { 130 : const std::string& measurement_name = 131 : control_system::combined_name<ControlSystems>(); 132 : ASSERT(measurement_timescales.count(measurement_name) == 1, 133 : "Control system trigger expects a measurement timescale " 134 : "with the name '" 135 : << measurement_name 136 : << "' but could not find one. Available names are: " 137 : << keys_of(measurement_timescales)); 138 : measurement_times->update( 139 : *measurement_timescales.at(measurement_name)); 140 : if (not measurement_times->next_measurement().has_value()) { 141 : if constexpr (Parallel::is_dg_element_collection_v<Component>) { 142 : // Note: The ArrayComponentId is still created with the 143 : // array_index (ElementId) because we only support 1 callback 144 : // per ArrayComponentId. This would mean for nodegroups we would 145 : // discard a lot of callbacks that we need. Alternatively, the 146 : // callback could do a broadcast to all elements on the 147 : // nodegroup. 148 : const auto element_location = static_cast<int>( 149 : Parallel::local_synchronous_action< 150 : Parallel::Actions::GetItemFromDistributedOject< 151 : Parallel::Tags::ElementLocations<Dim>>>( 152 : Parallel::get_parallel_component<Component>(cache)) 153 : ->at(array_index)); 154 : return std::unique_ptr<Parallel::Callback>( 155 : new Parallel::ThreadedActionCallback< 156 : Parallel::Actions::PerformAlgorithmOnElement<false>, 157 : decltype(proxy[element_location]), 158 : std::decay_t<decltype(array_index)>>{ 159 : proxy[element_location], array_index}); 160 : } else { 161 : return std::unique_ptr<Parallel::Callback>( 162 : new Parallel::PerformAlgorithmCallback(proxy[array_index])); 163 : } 164 : } 165 : return std::unique_ptr<Parallel::Callback>{}; 166 : }); 167 : 168 : if (not is_ready) { 169 : if (Parallel::get<Tags::Verbosity>(cache) >= ::Verbosity::Debug) { 170 : Parallel::printf( 171 : "%s, time = %.16f: Trigger for control systems (%s) - Cannot " 172 : "calculate next_check_time\n", 173 : get_output(array_index), time, 174 : pretty_type::list_of_names<ControlSystems>()); 175 : } 176 : return std::nullopt; 177 : } 178 : } 179 : 180 : const double next_trigger = *measurement_times->next_measurement(); 181 : ASSERT(next_trigger > time, 182 : "Next trigger is in the past: " << next_trigger << " > " << time); 183 : 184 : if (Parallel::get<Tags::Verbosity>(cache) >= ::Verbosity::Debug) { 185 : Parallel::printf( 186 : "%s, time = %.16f: Trigger for control systems (%s) - next check " 187 : "time is %.16f\n", 188 : get_output(array_index), time, 189 : pretty_type::list_of_names<ControlSystems>(), next_trigger); 190 : } 191 : 192 : return {next_trigger}; 193 : } 194 : }; 195 : 196 : /// \cond 197 : template <typename ControlSystems> 198 : PUP::able::PUP_ID Trigger<ControlSystems>::my_PUP_ID = 0; // NOLINT 199 : /// \endcond 200 : 201 : // This metafunction is tested in Test_EventTriggerMetafunctions.cpp 202 : 203 : /// \ingroup ControlSystemGroup 204 : /// The list of triggers needed for measurements for a list of control 205 : /// systems. 206 : template <typename ControlSystems> 207 1 : using control_system_triggers = tmpl::transform< 208 : metafunctions::measurements_t<ControlSystems>, 209 : tmpl::bind<Trigger, metafunctions::control_systems_with_measurement< 210 : tmpl::pin<ControlSystems>, tmpl::_1>>>; 211 : } // namespace control_system