Line data Source code
1 0 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : #pragma once
5 :
6 : #include <array>
7 : #include <limits>
8 : #include <memory>
9 : #include <optional>
10 : #include <string>
11 : #include <unordered_map>
12 :
13 : #include "ControlSystem/CalculateMeasurementTimescales.hpp"
14 : #include "ControlSystem/CombinedName.hpp"
15 : #include "ControlSystem/Controller.hpp"
16 : #include "ControlSystem/ExpirationTimes.hpp"
17 : #include "ControlSystem/Metafunctions.hpp"
18 : #include "ControlSystem/Tags/OptionTags.hpp"
19 : #include "ControlSystem/Tags/SystemTags.hpp"
20 : #include "ControlSystem/TimescaleTuner.hpp"
21 : #include "DataStructures/DataBox/Tag.hpp"
22 : #include "DataStructures/DataVector.hpp"
23 : #include "Domain/Creators/OptionTags.hpp"
24 : #include "Domain/FunctionsOfTime/FunctionOfTime.hpp"
25 : #include "Domain/FunctionsOfTime/PiecewisePolynomial.hpp"
26 : #include "Options/Auto.hpp"
27 : #include "Time/OptionTags/InitialTime.hpp"
28 : #include "Time/OptionTags/InitialTimeStep.hpp"
29 : #include "Utilities/ErrorHandling/Error.hpp"
30 : #include "Utilities/TMPL.hpp"
31 : #include "Utilities/TypeTraits/IsA.hpp"
32 :
33 : /// \cond
34 : template <class Metavariables, typename ControlSystem>
35 : struct ControlComponent;
36 : /// \endcond
37 :
38 : namespace control_system::Tags {
39 : /*!
40 : * \ingroup DataBoxTagsGroup
41 : * \ingroup ControlSystemGroup
42 : * \brief The measurement timescales associated with
43 : * domain::Tags::FunctionsOfTime.
44 : *
45 : * We group measurement timescales by
46 : * the `control_system::protocols::Measurement` that their corresponding control
47 : * systems use. This is because one measurement may be used to update many
48 : * functions of time. Each group of functions of time associated with a
49 : * particular measurement has a corresponding timescale here, represented as
50 : * `PiecewisePolynomial<0>` with a single entry. That single entry is the
51 : * minimum of all `control_system::calculate_measurement_timescales` for every
52 : * control system in that group.
53 : *
54 : * The name of a measurement timescale is calculated using
55 : * `control_system::system_to_combined_names` for every group of control systems
56 : * with the same measurement.
57 : *
58 : * If all control systems that use the same measurement aren't active, then the
59 : * measurement timescale and expiration time are
60 : * `std::numeric_limits<double>::infinity()`.
61 : */
62 1 : struct MeasurementTimescales : db::SimpleTag {
63 0 : using type = std::unordered_map<
64 : std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>;
65 :
66 : template <typename Metavariables>
67 0 : using option_holders = control_system::inputs<
68 : tmpl::transform<tmpl::filter<typename Metavariables::component_list,
69 : tt::is_a<ControlComponent, tmpl::_1>>,
70 : tmpl::bind<tmpl::back, tmpl::_1>>>;
71 :
72 0 : static constexpr bool pass_metavariables = true;
73 :
74 : template <typename Metavariables>
75 0 : using option_tags = tmpl::push_front<
76 : option_holders<Metavariables>,
77 : control_system::OptionTags::MeasurementsPerUpdate,
78 : control_system::OptionTags::DelayUpdate,
79 : domain::OptionTags::DomainCreator<Metavariables::volume_dim>,
80 : ::OptionTags::InitialTime, ::OptionTags::InitialTimeStep>;
81 :
82 : template <typename Metavariables, typename... OptionHolders>
83 0 : static type create_from_options(
84 : const int measurements_per_update, const bool delay_update,
85 : const std::unique_ptr<::DomainCreator<Metavariables::volume_dim>>&
86 : domain_creator,
87 : const double initial_time, const double initial_time_step,
88 : const Options::Auto<OptionHolders,
89 : Options::AutoLabel::None>&... option_holders) {
90 : return create_from_options<Metavariables>(
91 : measurements_per_update, delay_update, domain_creator, initial_time,
92 : initial_time_step,
93 : static_cast<std::optional<OptionHolders>>(option_holders)...);
94 : }
95 :
96 : template <typename Metavariables, typename... OptionHolders>
97 0 : static type create_from_options(
98 : const int measurements_per_update, const bool delay_update,
99 : const std::unique_ptr<::DomainCreator<Metavariables::volume_dim>>&
100 : domain_creator,
101 : const double initial_time, const double initial_time_step,
102 : const std::optional<OptionHolders>&... option_holders) {
103 : std::unordered_map<std::string,
104 : std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>
105 : timescales{};
106 :
107 : using control_systems =
108 : tmpl::list<typename OptionHolders::control_system...>;
109 :
110 : // First string is name of each control system. Second string is combination
111 : // of control systems with same measurement
112 : std::unordered_map<std::string, std::string> map_of_names =
113 : system_to_combined_names<control_systems>();
114 :
115 : // Initialize all measurements to infinity so we can take the min later
116 : std::unordered_map<std::string, double> min_measurement_timescales{};
117 : std::unordered_map<std::string, double> expiration_times{};
118 : for (const auto& [control_system_name, combined_name] : map_of_names) {
119 : (void)control_system_name;
120 : if (min_measurement_timescales.count(combined_name) != 1) {
121 : min_measurement_timescales[combined_name] =
122 : std::numeric_limits<double>::infinity();
123 : expiration_times[combined_name] =
124 : std::numeric_limits<double>::infinity();
125 : }
126 : }
127 :
128 : [[maybe_unused]] const auto combine_measurement_timescales =
129 : [&initial_time, &initial_time_step, &domain_creator,
130 : &measurements_per_update, &delay_update, &map_of_names,
131 : &min_measurement_timescales,
132 : &expiration_times](const auto& option_holder) {
133 : // This check is intentionally inside the lambda so that it will not
134 : // trigger for domains without control systems.
135 : if (initial_time_step <= 0.0) {
136 : ERROR(
137 : "Control systems can only be used in forward-in-time "
138 : "evolutions.");
139 : }
140 :
141 : const std::string& control_system_name = std::decay_t<
142 : decltype(option_holder)>::value_type::control_system::name();
143 : const std::string& combined_name = map_of_names[control_system_name];
144 :
145 : // If the control system isn't active, set measurement timescale and
146 : // expiration time to be infinity.
147 : double min_measurement_timescale =
148 : std::numeric_limits<double>::infinity();
149 :
150 : if (option_holder.has_value()) {
151 : auto tuner = option_holder->tuner;
152 : ::control_system::Tags::detail::initialize_tuner(
153 : make_not_null(&tuner), domain_creator, initial_time,
154 : control_system_name);
155 :
156 : const auto& controller = option_holder->controller;
157 : DataVector measurement_timescales =
158 : calculate_measurement_timescales(controller, tuner,
159 : measurements_per_update);
160 :
161 : min_measurement_timescale = min(measurement_timescales);
162 : }
163 :
164 : min_measurement_timescales[combined_name] =
165 : std::min(min_measurement_timescales[combined_name],
166 : min_measurement_timescale);
167 :
168 : if (min_measurement_timescales[combined_name] !=
169 : std::numeric_limits<double>::infinity()) {
170 : const double expiration_time = measurement_initial_expiration_time(
171 : initial_time,
172 : DataVector{1_st, min_measurement_timescales[combined_name]},
173 : measurements_per_update, delay_update);
174 : expiration_times[combined_name] =
175 : std::min(expiration_times[combined_name], expiration_time);
176 : }
177 : };
178 :
179 : EXPAND_PACK_LEFT_TO_RIGHT(combine_measurement_timescales(option_holders));
180 :
181 : for (const auto& [combined_name, min_measurement_timescale] :
182 : min_measurement_timescales) {
183 : double expiration_time = expiration_times[combined_name];
184 : timescales.emplace(
185 : combined_name,
186 : std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<0>>(
187 : initial_time,
188 : std::array{DataVector{1, min_measurement_timescale}},
189 : expiration_time));
190 : }
191 :
192 : return timescales;
193 : }
194 : };
195 : } // namespace control_system::Tags
|