Line data Source code
1 0 : // Distributed under the MIT License. 2 : // See LICENSE.txt for details. 3 : 4 : #pragma once 5 : 6 : #include <algorithm> 7 : #include <array> 8 : #include <cstddef> 9 : #include <tuple> 10 : #include <utility> 11 : 12 : #include "DataStructures/DataBox/DataBox.hpp" 13 : #include "DataStructures/DataBox/ObservationBox.hpp" 14 : #include "DataStructures/DataBox/TagTraits.hpp" 15 : #include "Domain/Amr/Flag.hpp" 16 : #include "Domain/Amr/Helpers.hpp" 17 : #include "Domain/Amr/Tags/Flags.hpp" 18 : #include "Domain/Structure/Element.hpp" 19 : #include "Domain/Structure/ElementId.hpp" 20 : #include "Domain/Structure/Neighbors.hpp" 21 : #include "Domain/Tags.hpp" 22 : #include "Parallel/ArrayCollection/IsDgElementCollection.hpp" 23 : #include "Parallel/ArrayCollection/SimpleActionOnElement.hpp" 24 : #include "Parallel/GlobalCache.hpp" 25 : #include "Parallel/Invoke.hpp" 26 : #include "Parallel/Tags/Section.hpp" 27 : #include "ParallelAlgorithms/Actions/GetItemFromDistributedObject.hpp" 28 : #include "ParallelAlgorithms/Amr/Actions/UpdateAmrDecision.hpp" 29 : #include "ParallelAlgorithms/Amr/Criteria/Criterion.hpp" 30 : #include "ParallelAlgorithms/Amr/Criteria/Tags/Criteria.hpp" 31 : #include "ParallelAlgorithms/Amr/Policies/EnforcePolicies.hpp" 32 : #include "ParallelAlgorithms/Amr/Policies/Policies.hpp" 33 : #include "ParallelAlgorithms/Amr/Policies/Tags.hpp" 34 : #include "ParallelAlgorithms/Amr/Projectors/Mesh.hpp" 35 : #include "ParallelAlgorithms/Amr/Tags.hpp" 36 : #include "Utilities/ErrorHandling/Assert.hpp" 37 : #include "Utilities/ErrorHandling/Error.hpp" 38 : #include "Utilities/Gsl.hpp" 39 : #include "Utilities/MakeArray.hpp" 40 : #include "Utilities/PrettyType.hpp" 41 : #include "Utilities/TMPL.hpp" 42 : 43 : /// \cond 44 : namespace tuples { 45 : template <class... Tags> 46 : class TaggedTuple; 47 : } // namespace tuples 48 : /// \endcond 49 : 50 : namespace detail { 51 : template <typename Criterion> 52 : struct get_tags { 53 : using type = typename Criterion::compute_tags_for_observation_box; 54 : }; 55 : 56 : } // namespace detail 57 : 58 : namespace amr::Actions { 59 : /// \brief Evaluates the refinement criteria in order to set the amr::Info of an 60 : /// Element and sends this information to the neighbors of the Element. 61 : /// 62 : /// DataBox: 63 : /// - Uses: 64 : /// * domain::Tags::Element<volume_dim> 65 : /// * amr::Tags::NeighborInfo<volume_dim> 66 : /// * amr::Criteria::Tags::Criteria (from GlobalCache) 67 : /// * amr::Tags::Policies (from GlobalCache) 68 : /// * any tags requested by the refinement criteria 69 : /// - Modifies: 70 : /// * amr::Tags::Info<volume_dim> 71 : /// 72 : /// Invokes: 73 : /// - UpdateAmrDecision on all neighboring Element%s 74 : /// 75 : /// \details 76 : /// - Evaluates each refinement criteria held by amr::Criteria::Tags::Criteria, 77 : /// and in each dimension selects the amr::Flag with the highest 78 : /// priority (i.e the highest integral value). If 79 : /// Metavariables::amr::p_refine_only_in_event is true, only h-refinement 80 : /// criteria will be evaluated 81 : /// - If necessary, changes the refinement decision in order to satisfy the 82 : /// amr::Policies 83 : /// - An Element that is splitting in one dimension is not allowed to join 84 : /// in another dimension. If this is requested by the refinement critiera, 85 : /// the decision to join is changed to do nothing 86 : /// - Checks if any neighbors have sent their AMR decision, and if so, calls 87 : /// amr:::update_amr_decision with the decision of each neighbor in 88 : /// order to see if the current decision needs to be updated 89 : /// - Sends the (possibly updated) decision to all of the neighboring Elements 90 1 : struct EvaluateRefinementCriteria { 91 : template <typename ParallelComponent, typename DbTagList, 92 : typename Metavariables, size_t Dim> 93 0 : static void apply(db::DataBox<DbTagList>& box, 94 : Parallel::GlobalCache<Metavariables>& cache, 95 : const ElementId<Dim>& element_id) { 96 : if constexpr (Metavariables::amr::keep_coarse_grids) { 97 : // Only evaluate the criteria on the finest grid. The other elements keep 98 : // their flags as Undefined and will therefore be skipped by AdjustDomain. 99 : const bool is_finest_grid = 100 : db::get<amr::Tags::ChildIds<Dim>>(box).empty(); 101 : if (not is_finest_grid) { 102 : return; 103 : } 104 : } 105 : 106 : constexpr size_t volume_dim = Metavariables::volume_dim; 107 : auto overall_decision = make_array<volume_dim>(amr::Flag::Undefined); 108 : 109 : // Evaluate criteria on blocks that have AMR enabled, and "do nothing" on 110 : // the other blocks. 111 : // Note that blocks that do not have AMR enabled can still be refined to 112 : // satisfy 2:1 balance. When this happens, they won't coarsen back since "do 113 : // nothing" has higher priority than coarsening. This behavior can be 114 : // changed if needed. 115 : const auto& amr_blocks = db::get<amr::Tags::AmrBlocks<volume_dim>>(box); 116 : if (not amr_blocks.has_value() or 117 : alg::found(amr_blocks.value(), element_id.block_id())) { 118 : using compute_tags = 119 : tmpl::remove_duplicates<tmpl::flatten<tmpl::transform< 120 : tmpl::at< 121 : typename Metavariables::factory_creation::factory_classes, 122 : Criterion>, 123 : detail::get_tags<tmpl::_1>>>>; 124 : auto observation_box = 125 : make_observation_box<compute_tags>(make_not_null(&box)); 126 : 127 : const auto& refinement_criteria = 128 : db::get<amr::Criteria::Tags::Criteria>(box); 129 : for (const auto& criterion : refinement_criteria) { 130 : const auto refinement_type = criterion->type(); 131 : if (Metavariables::amr::p_refine_only_in_event and 132 : refinement_type == amr::Criteria::Type::p) { 133 : continue; 134 : } 135 : auto decision = criterion->evaluate(observation_box, cache, element_id); 136 : if constexpr (Metavariables::amr::p_refine_only_in_event) { 137 : ASSERT(alg::none_of(decision, 138 : [](amr::Flag flag) { 139 : return flag == amr::Flag::IncreaseResolution or 140 : flag == amr::Flag::DecreaseResolution; 141 : }), 142 : "The criterion '" 143 : << typeid(*criterion).name() 144 : << "' requested p-refinement, but claims to be " 145 : "for h-refinement."); 146 : } 147 : 148 : // Enforce restrictions from the topologies of the Element 149 : const auto& topologies = 150 : db::get<domain::Tags::Element<Dim>>(box).topologies(); 151 : if (refinement_type == amr::Criteria::Type::p) { 152 : enforce_p_refinement_topology_restrictions(make_not_null(&decision), 153 : topologies); 154 : } else if (refinement_type == amr::Criteria::Type::h) { 155 : enforce_h_refinement_topology_restrictions(make_not_null(&decision), 156 : topologies); 157 : } else { 158 : ERROR("Bad refinement type " << refinement_type); 159 : } 160 : for (size_t d = 0; d < volume_dim; ++d) { 161 : overall_decision[d] = std::max(overall_decision[d], decision[d]); 162 : } 163 : } 164 : } // amr_blocks.contains(element_id.block_id()) 165 : 166 : // If no refinement criteria were called, then set flag to do nothing 167 : for (size_t d = 0; d < volume_dim; ++d) { 168 : if (overall_decision[d] == amr::Flag::Undefined) { 169 : overall_decision[d] = amr::Flag::DoNothing; 170 : } 171 : } 172 : 173 : const auto& policies = db::get<amr::Tags::Policies>(box); 174 : amr::enforce_policies(make_not_null(&overall_decision), policies, 175 : element_id, 176 : get<::domain::Tags::Mesh<volume_dim>>(box)); 177 : 178 : // An element cannot join if it is splitting in another dimension. 179 : // Update the flags now before sending to neighbors as each time 180 : // a flag is changed by UpdateAmrDecision, it sends the new flags 181 : // to its neighbors. So updating now will save some commmunication. 182 : amr::prevent_element_from_joining_while_splitting( 183 : make_not_null(&overall_decision)); 184 : 185 : // Check if we received any neighbor flags prior to determining our own 186 : // flags. If yes, then possible update our flags (e.g. sibling doesn't want 187 : // to join, maintain 2:1 balance, etc.) 188 : const auto& my_element = get<::domain::Tags::Element<volume_dim>>(box); 189 : const auto& my_neighbors_amr_info = 190 : get<amr::Tags::NeighborInfo<volume_dim>>(box); 191 : if (not my_neighbors_amr_info.empty()) { 192 : for (const auto& [neighbor_id, neighbor_amr_info] : 193 : my_neighbors_amr_info) { 194 : amr::update_amr_decision( 195 : make_not_null(&overall_decision), my_element, neighbor_id, 196 : neighbor_amr_info.flags, 197 : policies.enforce_two_to_one_balance_in_normal_direction()); 198 : } 199 : } 200 : 201 : const auto new_mesh = amr::projectors::new_mesh( 202 : get<::domain::Tags::Mesh<volume_dim>>(box), overall_decision, 203 : my_element, my_neighbors_amr_info); 204 : 205 : db::mutate<amr::Tags::Info<Metavariables::volume_dim>>( 206 : [&overall_decision, 207 : &new_mesh](const gsl::not_null<amr::Info<volume_dim>*> amr_info) { 208 : amr_info->flags = overall_decision; 209 : amr_info->new_mesh = new_mesh; 210 : }, 211 : make_not_null(&box)); 212 : 213 : auto& amr_element_array = 214 : Parallel::get_parallel_component<ParallelComponent>(cache); 215 : 216 : const amr::Info<Metavariables::volume_dim>& my_info = 217 : get<amr::Tags::Info<volume_dim>>(box); 218 : for (const auto& [direction, neighbors] : my_element.neighbors()) { 219 : (void)direction; 220 : for (const auto& neighbor_id : neighbors.ids()) { 221 : if constexpr (Parallel::is_dg_element_collection_v<ParallelComponent>) { 222 : const auto neighbor_location = static_cast<int>( 223 : Parallel::local_synchronous_action< 224 : Parallel::Actions::GetItemFromDistributedOject< 225 : Parallel::Tags::ElementLocations<volume_dim>>>( 226 : Parallel::get_parallel_component<ParallelComponent>(cache)) 227 : ->at(neighbor_id)); 228 : Parallel::threaded_action<Parallel::Actions::SimpleActionOnElement< 229 : UpdateAmrDecision, true>>(amr_element_array[neighbor_location], 230 : neighbor_id, element_id, my_info); 231 : } else { 232 : Parallel::simple_action<UpdateAmrDecision>( 233 : amr_element_array[neighbor_id], element_id, my_info); 234 : } 235 : } 236 : } 237 : } 238 : }; 239 : } // namespace amr::Actions