Line data Source code
1 0 : // Distributed under the MIT License. 2 : // See LICENSE.txt for details. 3 : 4 : #pragma once 5 : 6 : #include <deque> 7 : #include <limits> 8 : #include <memory> 9 : #include <sstream> 10 : #include <string> 11 : #include <type_traits> 12 : #include <unordered_map> 13 : 14 : #include "DataStructures/DataBox/DataBox.hpp" 15 : #include "DataStructures/LinkedMessageId.hpp" 16 : #include "Domain/Creators/Tags/Domain.hpp" 17 : #include "Domain/Domain.hpp" 18 : #include "IO/Logging/Verbosity.hpp" 19 : #include "Parallel/ArrayComponentId.hpp" 20 : #include "Parallel/Callback.hpp" 21 : #include "Parallel/GlobalCache.hpp" 22 : #include "Parallel/Invoke.hpp" 23 : #include "Parallel/ParallelComponentHelpers.hpp" 24 : #include "Parallel/Printf/Printf.hpp" 25 : #include "ParallelAlgorithms/Interpolation/Actions/SendPointsToInterpolator.hpp" 26 : #include "ParallelAlgorithms/Interpolation/InterpolationTargetDetail.hpp" 27 : #include "ParallelAlgorithms/Interpolation/Tags.hpp" 28 : #include "Utilities/Algorithm.hpp" 29 : #include "Utilities/Gsl.hpp" 30 : #include "Utilities/PrettyType.hpp" 31 : 32 : namespace intrp::Actions { 33 : /// \ingroup ActionsGroup 34 : /// \brief Sends points to an Interpolator for verified temporal_ids. 35 : /// 36 : /// VerifyTemporalIdsAndSendPoints is invoked on an InterpolationTarget. 37 : /// 38 : /// In more detail, does the following: 39 : /// - If any map is time-dependent: 40 : /// - If there is a CurrentTemporalId or no PendingTemporalIds, does nothing 41 : /// - If any map is time-dependent: 42 : /// Moves first verified PendingTemporalId to CurrentTemporalId, where 43 : /// verified means that the FunctionsOfTime in the GlobalCache 44 : /// are up-to-date for that TemporalId. If no PendingTemporalIds are 45 : /// moved, then VerifyTemporalIdsAndSendPoints sets itself as a 46 : /// callback in the GlobalCache so that it is called again when the 47 : /// FunctionsOfTime are mutated. 48 : /// - If maps are time-independent: 49 : /// Verified means just the first id in PendingTemporalIds 50 : /// - If the temporal id type is a LinkedMessageId, determines if this 51 : /// verified id is in order. If it isn't, then this just returns and does 52 : /// nothing 53 : /// - Calls intrp::Actions::SendPointsToInterpolator directly for this 54 : /// verified TemporalId. (when interpolation is complete, 55 : /// intrp::Actions::InterpolationTargetReceiveVars will begin interpolation 56 : /// on the next verified Id) 57 : /// 58 : /// Uses: 59 : /// - DataBox: 60 : /// - `intrp::Tags::PendingTemporalIds` 61 : /// - `intrp::Tags::CompletedTemporalIds` 62 : /// - `intrp::Tags::CurrentTemporalId` 63 : /// 64 : /// DataBox changes: 65 : /// - Adds: nothing 66 : /// - Removes: nothing 67 : /// - Modifies: 68 : /// - `intrp::Tags::PendingTemporalIds` 69 : /// - `intrp::Tags::CurrentTemporalId` 70 : /// 71 : /// For requirements on InterpolationTargetTag, see InterpolationTarget 72 : template <typename InterpolationTargetTag> 73 1 : struct VerifyTemporalIdsAndSendPoints { 74 : template <typename ParallelComponent, typename DbTags, typename Metavariables, 75 : typename ArrayIndex> 76 0 : static void apply(db::DataBox<DbTags>& box, 77 : Parallel::GlobalCache<Metavariables>& cache, 78 : const ArrayIndex& array_index) { 79 : static_assert( 80 : InterpolationTargetTag::compute_target_points::is_sequential::value, 81 : "Actions::VerifyTemporalIdsAndSendPoints can be used only with " 82 : "sequential targets."); 83 : 84 : using TemporalId = typename InterpolationTargetTag::temporal_id::type; 85 : 86 : std::stringstream ss{}; 87 : const ::Verbosity& verbosity = Parallel::get<intrp::Tags::Verbosity>(cache); 88 : const bool verbose_print = verbosity >= ::Verbosity::Verbose; 89 : if (verbose_print) { 90 : ss << InterpolationTarget_detail::target_output_prefix< 91 : VerifyTemporalIdsAndSendPoints<InterpolationTargetTag>, 92 : InterpolationTargetTag>() 93 : << ", "; 94 : } 95 : 96 : const auto& pending_temporal_ids = 97 : db::get<Tags::PendingTemporalIds<TemporalId>>(box); 98 : // Nothing to do if there's an interpolation in progress or there are no 99 : // pending temporal_ids. 100 : if (db::get<Tags::CurrentTemporalId<TemporalId>>(box).has_value() or 101 : pending_temporal_ids.empty()) { 102 : if (verbose_print) { 103 : if (db::get<Tags::CurrentTemporalId<TemporalId>>(box).has_value()) { 104 : ss << "Interpolation already in progess at id " 105 : << db::get<Tags::CurrentTemporalId<TemporalId>>(box).value(); 106 : } else { 107 : ss << "No pending temporal ids to send points at."; 108 : } 109 : Parallel::printf("%s\n", ss.str()); 110 : } 111 : 112 : return; 113 : } 114 : 115 : const auto& domain = 116 : get<domain::Tags::Domain<Metavariables::volume_dim>>(cache); 117 : 118 : // Account for time dependent maps. If the points are in the grid frame or 119 : // if we don't have FunctionsOfTime in the cache, then we don't have to 120 : // check the FunctionsOfTime. 121 : if constexpr (not std::is_same_v<typename InterpolationTargetTag:: 122 : compute_target_points::frame, 123 : ::Frame::Grid> and 124 : Parallel::is_in_global_cache<Metavariables, 125 : domain::Tags::FunctionsOfTime>) { 126 : if (domain.is_time_dependent()) { 127 : const auto& next_pending_id = pending_temporal_ids.front(); 128 : 129 : auto& this_proxy = 130 : Parallel::get_parallel_component<ParallelComponent>(cache); 131 : double min_expiration_time = std::numeric_limits<double>::max(); 132 : const Parallel::ArrayComponentId array_component_id = 133 : Parallel::make_array_component_id<ParallelComponent>(array_index); 134 : const bool next_pending_id_is_ready = [&cache, &array_component_id, 135 : &this_proxy, &next_pending_id, 136 : &min_expiration_time]() { 137 : return ::Parallel::mutable_cache_item_is_ready< 138 : domain::Tags::FunctionsOfTime>( 139 : cache, array_component_id, 140 : [&this_proxy, &next_pending_id, &min_expiration_time]( 141 : const std::unordered_map< 142 : std::string, 143 : std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>& 144 : functions_of_time) 145 : -> std::unique_ptr<Parallel::Callback> { 146 : min_expiration_time = 147 : std::min_element(functions_of_time.begin(), 148 : functions_of_time.end(), 149 : [](const auto& a, const auto& b) { 150 : return a.second->time_bounds()[1] < 151 : b.second->time_bounds()[1]; 152 : }) 153 : ->second->time_bounds()[1]; 154 : 155 : // Success: the next pending_temporal_id is ok. 156 : // Failure: the next pending_temporal_id is not ok. 157 : return InterpolationTarget_detail::get_temporal_id_value( 158 : next_pending_id) <= min_expiration_time 159 : ? std::unique_ptr<Parallel::Callback>{} 160 : : std::unique_ptr<Parallel::Callback>( 161 : new Parallel::SimpleActionCallback< 162 : VerifyTemporalIdsAndSendPoints< 163 : InterpolationTargetTag>, 164 : decltype(this_proxy)>(this_proxy)); 165 : }); 166 : }(); 167 : 168 : if (not next_pending_id_is_ready) { 169 : if (verbose_print) { 170 : ss << "The next pending temporal id " << next_pending_id 171 : << " is not ready."; 172 : Parallel::printf("%s\n", ss.str()); 173 : } 174 : 175 : // A callback has been set so that VerifyTemporalIdsAndSendPoints will 176 : // be called by the GlobalCache when domain::Tags::FunctionsOfTime is 177 : // updated. So we can exit now. 178 : return; 179 : } 180 : } // if (domain.is_time_dependent()) 181 : } else if constexpr (not std::is_same_v<typename InterpolationTargetTag:: 182 : compute_target_points::frame, 183 : ::Frame::Grid> and 184 : not Parallel::is_in_global_cache< 185 : Metavariables, domain::Tags::FunctionsOfTime>) { 186 : if (domain.is_time_dependent()) { 187 : // We error here because the maps are time-dependent, yet 188 : // the cache does not contain FunctionsOfTime. It would be 189 : // nice to make this a compile-time error; however, we want 190 : // the code to compile for the completely time-independent 191 : // case where there are no FunctionsOfTime in the cache at 192 : // all. Unfortunately, checking whether the maps are 193 : // time-dependent is currently not constexpr. 194 : ERROR( 195 : "There is a time-dependent CoordinateMap in at least one " 196 : "of the Blocks, but FunctionsOfTime are not in the " 197 : "GlobalCache. If you intend to use a time-dependent " 198 : "CoordinateMap, please add FunctionsOfTime to the GlobalCache."); 199 : } 200 : } 201 : 202 : // Move the most recent PendingTemporalId to CurrentTemporalId. If this is a 203 : // LinkedMessageId, make sure this is the next id before sending points 204 : db::mutate_apply<tmpl::list<Tags::CurrentTemporalId<TemporalId>, 205 : Tags::PendingTemporalIds<TemporalId>>, 206 : tmpl::list<Tags::CompletedTemporalIds<TemporalId>>>( 207 : [](const gsl::not_null<std::optional<TemporalId>*> current_id, 208 : const gsl::not_null<std::deque<TemporalId>*> pending_ids, 209 : const std::deque<TemporalId>& completed_ids) { 210 : auto& next_pending_id = pending_ids->front(); 211 : bool use_next_pending_id = true; 212 : 213 : if constexpr (std::is_same_v<LinkedMessageId<double>, TemporalId>) { 214 : // If completed ids is empty (and at this point so is temporal ids) 215 : // then we must check if this is the first id. 216 : use_next_pending_id = completed_ids.empty() 217 : ? not next_pending_id.previous.has_value() 218 : : next_pending_id.previous.value() == 219 : completed_ids.back().id; 220 : } 221 : 222 : if (use_next_pending_id) { 223 : *current_id = next_pending_id; 224 : pending_ids->pop_front(); 225 : } 226 : }, 227 : make_not_null(&box)); 228 : 229 : // Send points to interpolator if the next id is ready 230 : if (const auto& current_id = 231 : db::get<Tags::CurrentTemporalId<TemporalId>>(box); 232 : current_id.has_value()) { 233 : if (verbose_print) { 234 : ss << "Going to send points to interpolator at temporal id " 235 : << current_id.value(); 236 : } 237 : Actions::SendPointsToInterpolator<InterpolationTargetTag>::template apply< 238 : ParallelComponent>(box, cache, array_index, current_id.value()); 239 : } else if (verbose_print) { 240 : ss << "No temporal ids to send points at."; 241 : } 242 : 243 : if (verbose_print) { 244 : Parallel::printf("%s\n", ss.str()); 245 : } 246 : } 247 : }; 248 : } // namespace intrp::Actions