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_to_communicate_to_neighbors(
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
|