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