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
|