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 <cstddef>
8 : #include <deque>
9 : #include <iterator>
10 : #include <optional>
11 : #include <tuple>
12 : #include <type_traits>
13 : #include <utility>
14 :
15 : #include "DataStructures/DataBox/DataBox.hpp"
16 : #include "DataStructures/DataBox/PrefixHelpers.hpp"
17 : #include "DataStructures/DataBox/Prefixes.hpp"
18 : #include "DataStructures/DataVector.hpp"
19 : #include "DataStructures/TaggedTuple.hpp"
20 : #include "Domain/Structure/Direction.hpp"
21 : #include "Domain/Structure/DirectionalId.hpp"
22 : #include "Domain/Structure/DirectionalIdMap.hpp"
23 : #include "Domain/Structure/Element.hpp"
24 : #include "Domain/Structure/ElementId.hpp"
25 : #include "Domain/Tags.hpp"
26 : #include "Evolution/DgSubcell/Actions/Labels.hpp"
27 : #include "Evolution/DgSubcell/ActiveGrid.hpp"
28 : #include "Evolution/DgSubcell/GhostData.hpp"
29 : #include "Evolution/DgSubcell/Mesh.hpp"
30 : #include "Evolution/DgSubcell/NeighborRdmpAndVolumeData.hpp"
31 : #include "Evolution/DgSubcell/Projection.hpp"
32 : #include "Evolution/DgSubcell/RdmpTci.hpp"
33 : #include "Evolution/DgSubcell/RdmpTciData.hpp"
34 : #include "Evolution/DgSubcell/SubcellOptions.hpp"
35 : #include "Evolution/DgSubcell/Tags/ActiveGrid.hpp"
36 : #include "Evolution/DgSubcell/Tags/Coordinates.hpp"
37 : #include "Evolution/DgSubcell/Tags/DataForRdmpTci.hpp"
38 : #include "Evolution/DgSubcell/Tags/DidRollback.hpp"
39 : #include "Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp"
40 : #include "Evolution/DgSubcell/Tags/Interpolators.hpp"
41 : #include "Evolution/DgSubcell/Tags/Mesh.hpp"
42 : #include "Evolution/DgSubcell/Tags/MeshForGhostData.hpp"
43 : #include "Evolution/DgSubcell/Tags/SubcellOptions.hpp"
44 : #include "Evolution/DgSubcell/Tags/TciStatus.hpp"
45 : #include "Evolution/DiscontinuousGalerkin/InboxTags.hpp"
46 : #include "NumericalAlgorithms/Interpolation/IrregularInterpolant.hpp"
47 : #include "NumericalAlgorithms/Spectral/Mesh.hpp"
48 : #include "Parallel/AlgorithmExecution.hpp"
49 : #include "Parallel/GlobalCache.hpp"
50 : #include "ParallelAlgorithms/Actions/Goto.hpp"
51 : #include "Time/Actions/SelfStartActions.hpp"
52 : #include "Time/History.hpp"
53 : #include "Utilities/Algorithm.hpp"
54 : #include "Utilities/ContainerHelpers.hpp"
55 : #include "Utilities/ErrorHandling/Assert.hpp"
56 : #include "Utilities/TMPL.hpp"
57 :
58 : /// \cond
59 : namespace Tags {
60 : template <typename Tag>
61 : struct HistoryEvolvedVariables;
62 : struct TimeStepId;
63 : } // namespace Tags
64 : /// \endcond
65 :
66 : namespace evolution::dg::subcell::Actions {
67 : /*!
68 : * \brief Run the troubled-cell indicator on the candidate solution and perform
69 : * the time step rollback if needed.
70 : *
71 : * Elements that cannot use subcell (e.g. non-hypercube topology or DG-only
72 : * blocks) skip the TCI entirely, set the TCI decision to 0, clear ghost data,
73 : * and continue.
74 : *
75 : * Interior cells are marked as troubled if
76 : * `subcell_options.always_use_subcells()` is `true`, or if either the RDMP
77 : * troubled-cell indicator (TCI) or the TciMutator reports the cell is
78 : * troubled. Exterior cells are marked as troubled only if
79 : * `Metavariables::SubcellOptions::subcell_enabled_at_external_boundary` is
80 : * `true`.
81 : *
82 : * The troubled-cell indicator (TCI) given by the mutator `TciMutator` can
83 : * mutate tags in the DataBox, but should do so cautiously. The main reason that
84 : * this is a mutator is because primitive variables, such as the pressure, are
85 : * used to check if the solution is physical. In the relativistic case, even
86 : * just whether or not the primitive variables can be recovered is used as a
87 : * condition. Note that the evolved variables are projected to the subcells
88 : * _after_ the TCI is called and marks the cell as troubled.
89 : *
90 : * After rollback, the subcell scheme must project the DG boundary corrections
91 : * \f$G\f$ to the subcells for the scheme to be conservative. The subcell
92 : * actions know if a rollback was done because the local mortar data would
93 : * already be computed.
94 : */
95 : template <typename TciMutator>
96 1 : struct TciAndRollback {
97 : template <typename DbTags, typename... InboxTags, typename Metavariables,
98 : typename ArrayIndex, typename ActionList,
99 : typename ParallelComponent, size_t Dim = Metavariables::volume_dim>
100 0 : static Parallel::iterable_action_return_t apply(
101 : db::DataBox<DbTags>& box,
102 : const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
103 : const Parallel::GlobalCache<Metavariables>& /*cache*/,
104 : const ArrayIndex& /*array_index*/, const ActionList /*meta*/,
105 : const ParallelComponent* const /*meta*/) {
106 : static_assert(
107 : tmpl::count_if<
108 : ActionList,
109 : std::is_same<tmpl::_1, tmpl::pin<TciAndRollback>>>::value == 1,
110 : "Must have the TciAndRollback action exactly once in the action list "
111 : "of a phase.");
112 : static_assert(
113 : tmpl::count_if<
114 : ActionList,
115 : std::is_same<tmpl::_1,
116 : tmpl::pin<::Actions::Label<
117 : evolution::dg::subcell::Actions::Labels::
118 : BeginSubcellAfterDgRollback>>>>::value == 1,
119 : "Must have the BeginSubcellAfterDgRollback label exactly once in the "
120 : "action list of a phase.");
121 :
122 : using variables_tag = typename Metavariables::system::variables_tag;
123 :
124 : const ActiveGrid active_grid = db::get<Tags::ActiveGrid>(box);
125 : ASSERT(active_grid == ActiveGrid::Dg,
126 : "Must be using DG when calling TciAndRollback action.");
127 :
128 : const Element<Dim>& element = db::get<::domain::Tags::Element<Dim>>(box);
129 : const bool cell_has_external_boundary =
130 : not element.external_boundaries().empty();
131 :
132 : constexpr bool subcell_enabled_at_external_boundary =
133 : Metavariables::SubcellOptions::subcell_enabled_at_external_boundary;
134 : const Mesh<Dim>& dg_mesh = db::get<::domain::Tags::Mesh<Dim>>(box);
135 : const Mesh<Dim>& subcell_mesh = db::get<Tags::Mesh<Dim>>(box);
136 :
137 : const SubcellOptions& subcell_options =
138 : db::get<Tags::SubcellOptions<Dim>>(box);
139 : bool cell_is_troubled =
140 : subcell_options.always_use_subcells() or
141 : (subcell_options.use_halo() and [&box]() -> bool {
142 : for (const auto& [_, neighbor_decision] :
143 : db::get<evolution::dg::subcell::Tags::NeighborTciDecisions<Dim>>(
144 : box)) {
145 : if (neighbor_decision != 0) {
146 : return true;
147 : }
148 : }
149 : return false;
150 : }());
151 :
152 : // Loop over block neighbors and if neighbor id is inside of
153 : // subcell_options.only_dg_block_ids(), then bordering DG-only block
154 : const bool bordering_dg_block = alg::any_of(
155 : element.neighbors(),
156 : [&subcell_options](const auto& direction_and_neighbor) {
157 : const size_t first_block_id =
158 : direction_and_neighbor.second.ids().begin()->block_id();
159 : return alg::found(subcell_options.only_dg_block_ids(),
160 : first_block_id);
161 : });
162 :
163 : // Subcell is allowed in the element if 3 conditions are met:
164 : // (i) The DG mesh topology supports subcell
165 : // (ii) The current element block id is not marked as DG only
166 : // (iii) The current element is not bordering a DG only block.
167 : const bool subcell_allowed_in_element =
168 : fd::dg_mesh_supports_subcell(dg_mesh) and
169 : not alg::found(subcell_options.only_dg_block_ids(),
170 : element.id().block_id()) and
171 : not bordering_dg_block;
172 :
173 : // Elements that can never use subcell (e.g. non-hypercube topology or
174 : // DG-only blocks) should skip the TCI entirely, since the TCI projects
175 : // to the subcell mesh which is unsupported for these elements.
176 : if (not subcell_allowed_in_element) {
177 : db::mutate<Tags::TciDecision,
178 : subcell::Tags::GhostDataForReconstruction<Dim>>(
179 : [](const gsl::not_null<int*> tci_decision_ptr,
180 : const auto neighbor_data_ptr) {
181 : *tci_decision_ptr = 0;
182 : neighbor_data_ptr->clear();
183 : },
184 : make_not_null(&box));
185 : return {Parallel::AlgorithmExecution::Continue, std::nullopt};
186 : }
187 :
188 : // The reason we pass in the persson_exponent explicitly instead of
189 : // leaving it to the user is because the value of the exponent that
190 : // should be used to decide if it is safe to switch back to DG should be
191 : // `persson_exponent+1` to prevent the code from rapidly switching back
192 : // and forth between DG and subcell. Rather than trying to enforce this
193 : // by documentation, the switching back to DG TCI gets passed in the
194 : // exponent it should use, and to keep the interface between the TCIs
195 : // consistent, we also pass the exponent in separately here.
196 : std::tuple<int, RdmpTciData> tci_result = db::mutate_apply<TciMutator>(
197 : make_not_null(&box), subcell_options.persson_exponent(), false);
198 :
199 : const int tci_decision = std::get<0>(tci_result);
200 : db::mutate<Tags::TciDecision>(
201 : [&tci_decision](const gsl::not_null<int*> tci_decision_ptr) {
202 : *tci_decision_ptr = tci_decision;
203 : },
204 : make_not_null(&box));
205 :
206 : cell_is_troubled |= (tci_decision != 0);
207 :
208 : // If either:
209 : //
210 : // 1. we are not allowed to do subcell in this block (handled by the
211 : // early return above)
212 : // 2. the element is at an outer boundary _and_ we aren't allowed to go
213 : // to subcell at an outer boundary.
214 : // 3. the cell is not troubled
215 : //
216 : // then we can remove the current neighbor data and update the RDMP TCI
217 : // data.
218 : if ((cell_has_external_boundary and
219 : not subcell_enabled_at_external_boundary) or
220 : not cell_is_troubled) {
221 : db::mutate<subcell::Tags::GhostDataForReconstruction<Dim>,
222 : subcell::Tags::DataForRdmpTci>(
223 : [&tci_result](const auto neighbor_data_ptr,
224 : const gsl::not_null<RdmpTciData*> rdmp_tci_data_ptr) {
225 : neighbor_data_ptr->clear();
226 : *rdmp_tci_data_ptr = std::move(std::get<1>(std::move(tci_result)));
227 : },
228 : make_not_null(&box));
229 : return {Parallel::AlgorithmExecution::Continue, std::nullopt};
230 : }
231 :
232 : db::mutate<variables_tag, ::Tags::HistoryEvolvedVariables<variables_tag>,
233 : Tags::ActiveGrid, Tags::DidRollback,
234 : subcell::Tags::GhostDataForReconstruction<Dim>>(
235 : [&dg_mesh, &element, &subcell_mesh](
236 : const auto active_vars_ptr, const auto active_history_ptr,
237 : const gsl::not_null<ActiveGrid*> active_grid_ptr,
238 : const gsl::not_null<bool*> did_rollback_ptr,
239 : const gsl::not_null<DirectionalIdMap<Dim, GhostData>*>
240 : ghost_data_ptr,
241 : const DirectionalIdMap<Dim, Mesh<Dim>>& meshes_for_ghost_data,
242 : const size_t ghost_zone_size,
243 : const DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>&
244 : neighbor_dg_to_fd_interpolants) {
245 : ASSERT(active_history_ptr->size() > 0,
246 : "We cannot have an empty history when unwinding, that's just "
247 : "nutty. Did you call the action too early in the action "
248 : "list?");
249 : // Rollback u^{n+1}* to u^n (undoing the candidate solution).
250 : //
251 : // Note: strictly speaking, to be conservative this should project
252 : // uJ instead of u.
253 : *active_vars_ptr = fd::project(active_history_ptr->latest_value(),
254 : dg_mesh, subcell_mesh.extents());
255 :
256 : // Project the time stepper history to the subcells, excluding the
257 : // most recent inadmissible history.
258 : active_history_ptr->undo_latest();
259 : active_history_ptr->map_entries(
260 : [&dg_mesh, &subcell_mesh](const auto entry) {
261 : *entry = fd::project(*entry, dg_mesh, subcell_mesh.extents());
262 : });
263 : *active_grid_ptr = ActiveGrid::Subcell;
264 : *did_rollback_ptr = true;
265 : // Project the neighbor data we were sent for reconstruction since
266 : // the neighbor might have sent DG volume data instead of ghost data
267 : // in order to elide projections when they aren't necessary.
268 : for (const auto& [directional_element_id, mesh_for_ghost_data] :
269 : meshes_for_ghost_data) {
270 : evolution::dg::subcell::insert_or_update_neighbor_volume_data<
271 : false>(ghost_data_ptr,
272 : ghost_data_ptr->at(directional_element_id)
273 : .neighbor_ghost_data_for_reconstruction(),
274 : 0, directional_element_id, mesh_for_ghost_data, element,
275 : subcell_mesh, ghost_zone_size,
276 : neighbor_dg_to_fd_interpolants,
277 : typename Metavariables::SubcellOptions::GhostVariables::
278 : ghost_variables_tag_list{});
279 : }
280 :
281 : // Note: We do _not_ project the boundary history here because
282 : // that needs to be done at the lifting stage of the subcell
283 : // method, since we need to lift G+D instead of the ingredients
284 : // that go into G+D, which is what we would be projecting here.
285 : },
286 : make_not_null(&box),
287 : db::get<evolution::dg::subcell::Tags::MeshForGhostData<Dim>>(box),
288 : Metavariables::SubcellOptions::ghost_zone_size(box),
289 : db::get<
290 : evolution::dg::subcell::Tags::InterpolatorsFromNeighborDgToFd<Dim>>(
291 : box));
292 :
293 : if (UNLIKELY(db::get<::Tags::TimeStepId>(box).slab_number() < 0)) {
294 : // If we are doing self start, then we need to project the initial
295 : // guess to the subcells as well.
296 : //
297 : // Warning: this unfortunately needs to be kept in sync with the
298 : // self-start procedure.
299 : //
300 : // Note: if we switch to the subcells then we might have an
301 : // inconsistent
302 : // state between the primitive and conservative variables on the
303 : // subcells. The most correct thing is to re-compute the
304 : // primitive variables on the subcells, since projecting the
305 : // conservative variables is conservative.
306 : if constexpr (Metavariables::system::
307 : has_primitive_and_conservative_vars) {
308 : db::mutate<
309 : SelfStart::Tags::InitialValue<variables_tag>,
310 : SelfStart::Tags::InitialValue<
311 : typename Metavariables::system::primitive_variables_tag>>(
312 : [&dg_mesh, &subcell_mesh](const auto initial_vars_ptr,
313 : const auto initial_prim_vars_ptr) {
314 : // Note: for strict conservation, we need to project uJ
315 : // instead of just u.
316 : std::get<0>(*initial_vars_ptr) =
317 : fd::project(std::get<0>(*initial_vars_ptr), dg_mesh,
318 : subcell_mesh.extents());
319 : std::get<0>(*initial_prim_vars_ptr) =
320 : fd::project(std::get<0>(*initial_prim_vars_ptr), dg_mesh,
321 : subcell_mesh.extents());
322 : },
323 : make_not_null(&box));
324 : } else {
325 : db::mutate<SelfStart::Tags::InitialValue<variables_tag>>(
326 : [&dg_mesh, &subcell_mesh](const auto initial_vars_ptr) {
327 : // Note: for strict conservation, we need to project uJ
328 : // instead of just u.
329 : std::get<0>(*initial_vars_ptr) =
330 : fd::project(std::get<0>(*initial_vars_ptr), dg_mesh,
331 : subcell_mesh.extents());
332 : },
333 : make_not_null(&box));
334 : }
335 : }
336 :
337 : return {Parallel::AlgorithmExecution::Continue,
338 : tmpl::index_of<
339 : ActionList,
340 : ::Actions::Label<evolution::dg::subcell::Actions::Labels::
341 : BeginSubcellAfterDgRollback>>::value +
342 : 1};
343 : }
344 : };
345 : } // namespace evolution::dg::subcell::Actions
|