Line data Source code
1 0 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : #pragma once
5 :
6 : #include <cstddef>
7 : #include <map>
8 : #include <optional>
9 : #include <unordered_map>
10 : #include <unordered_set>
11 :
12 : #include "DataStructures/DataBox/DataBox.hpp"
13 : #include "DataStructures/DataBox/PrefixHelpers.hpp"
14 : #include "DataStructures/FixedHashMap.hpp"
15 : #include "DataStructures/Matrix.hpp"
16 : #include "DataStructures/TaggedTuple.hpp"
17 : #include "Domain/Creators/Tags/InitialRefinementLevels.hpp"
18 : #include "Domain/Structure/ChildSize.hpp"
19 : #include "Domain/Structure/ElementId.hpp"
20 : #include "IO/Logging/Tags.hpp"
21 : #include "IO/Observer/Tags.hpp"
22 : #include "NumericalAlgorithms/Convergence/Tags.hpp"
23 : #include "NumericalAlgorithms/Spectral/Projection.hpp"
24 : #include "Parallel/AlgorithmExecution.hpp"
25 : #include "Parallel/GlobalCache.hpp"
26 : #include "Parallel/InboxInserters.hpp"
27 : #include "Parallel/Invoke.hpp"
28 : #include "Parallel/Printf/Printf.hpp"
29 : #include "ParallelAlgorithms/Actions/Goto.hpp"
30 : #include "ParallelAlgorithms/Amr/Protocols/Projector.hpp"
31 : #include "ParallelAlgorithms/Amr/Tags.hpp"
32 : #include "ParallelAlgorithms/LinearSolver/Multigrid/Actions/RestrictFields.hpp"
33 : #include "ParallelAlgorithms/LinearSolver/Multigrid/Hierarchy.hpp"
34 : #include "ParallelAlgorithms/LinearSolver/Multigrid/Tags.hpp"
35 : #include "ParallelAlgorithms/LinearSolver/Tags.hpp"
36 : #include "Utilities/ConstantExpressions.hpp"
37 : #include "Utilities/ErrorHandling/Assert.hpp"
38 : #include "Utilities/GetOutput.hpp"
39 : #include "Utilities/PrettyType.hpp"
40 : #include "Utilities/ProtocolHelpers.hpp"
41 : #include "Utilities/TypeTraits/IsA.hpp"
42 :
43 : namespace LinearSolver::multigrid::detail {
44 :
45 : /// \cond
46 : template <typename FieldsTag, typename OptionsGroup, typename SourceTag>
47 : struct SendCorrectionToFinerGrid;
48 : template <typename FieldsTag, typename OptionsGroup, typename SourceTag>
49 : struct SkipBottomSolver;
50 : template <typename FieldsTag, typename OptionsGroup, typename SourceTag>
51 : struct SkipPostSmoothingAtBottom;
52 : /// \endcond
53 :
54 : struct PostSmoothingBeginLabel {};
55 :
56 : template <size_t Dim, typename FieldsTag, typename OptionsGroup,
57 : typename SourceTag>
58 : struct InitializeElement : tt::ConformsTo<amr::protocols::Projector> {
59 : private:
60 : using VolumeDataVars =
61 : typename Tags::VolumeDataForOutput<OptionsGroup, FieldsTag>::type;
62 :
63 : public: // Iterable action
64 : using simple_tags_from_options =
65 : tmpl::list<Tags::ChildrenRefinementLevels<Dim>,
66 : Tags::ParentRefinementLevels<Dim>>;
67 : using simple_tags =
68 : tmpl::list<amr::Tags::ParentId<Dim>, amr::Tags::ChildIds<Dim>,
69 : amr::Tags::ParentMesh<Dim>,
70 : LinearSolver::Tags::ObservationId<OptionsGroup>,
71 : Tags::VolumeDataForOutput<OptionsGroup, FieldsTag>>;
72 : using compute_tags = tmpl::list<>;
73 : using const_global_cache_tags =
74 : tmpl::list<Tags::InitialCoarseLevels<OptionsGroup>,
75 : LinearSolver::Tags::OutputVolumeData<OptionsGroup>>;
76 :
77 : template <typename DbTagsList, typename... InboxTags, typename Metavariables,
78 : typename ActionList, typename ParallelComponent>
79 : static Parallel::iterable_action_return_t apply(
80 : db::DataBox<DbTagsList>& box,
81 : const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
82 : const Parallel::GlobalCache<Metavariables>& /*cache*/,
83 : const ElementId<Dim>& /*element_id*/, const ActionList /*meta*/,
84 : const ParallelComponent* const /*meta*/) {
85 : db::mutate_apply<InitializeElement>(make_not_null(&box));
86 : return {Parallel::AlgorithmExecution::Continue, std::nullopt};
87 : }
88 :
89 : public: // amr::protocols::Projector
90 : using argument_tags =
91 : tmpl::list<domain::Tags::Mesh<Dim>, domain::Tags::Element<Dim>,
92 : domain::Tags::InitialRefinementLevels<Dim>,
93 : LinearSolver::Tags::OutputVolumeData<OptionsGroup>>;
94 : using return_tags = tmpl::append<simple_tags, simple_tags_from_options>;
95 :
96 : template <typename... AmrData>
97 : static void apply(
98 : const gsl::not_null<std::optional<ElementId<Dim>>*> parent_id,
99 : const gsl::not_null<std::unordered_set<ElementId<Dim>>*> child_ids,
100 : const gsl::not_null<std::optional<Mesh<Dim>>*> parent_mesh,
101 : const gsl::not_null<size_t*> observation_id,
102 : const gsl::not_null<VolumeDataVars*> volume_data_for_output,
103 : const gsl::not_null<std::vector<std::array<size_t, Dim>>*>
104 : children_refinement_levels,
105 : const gsl::not_null<std::vector<std::array<size_t, Dim>>*>
106 : parent_refinement_levels,
107 : const Mesh<Dim>& mesh, const Element<Dim>& element,
108 : const std::vector<std::array<size_t, Dim>> initial_refinement_levels,
109 : const bool output_volume_data, const AmrData&... amr_data) {
110 : // Note: This initialization code runs on elements of the initial multigrid
111 : // hierarchy (created by LinearSolver::multigrid::ElementsAllocator) and on
112 : // elements created by AMR. Elements in a block of the initial domain are
113 : // assumed to have the same p-refinement.
114 :
115 : if constexpr (sizeof...(AmrData) == 0) {
116 : // Initialization: use initial domain to set up multigrid hierarchy
117 : const auto& element_id = element.id();
118 : const bool is_coarsest_grid =
119 : initial_refinement_levels == *parent_refinement_levels;
120 : const bool is_finest_grid =
121 : initial_refinement_levels == *children_refinement_levels;
122 : *parent_id = is_coarsest_grid
123 : ? std::nullopt
124 : : std::make_optional(multigrid::parent_id(element_id));
125 : *child_ids =
126 : is_finest_grid
127 : ? std::unordered_set<ElementId<Dim>>{}
128 : : multigrid::child_ids(
129 : element_id,
130 : (*children_refinement_levels)[element_id.block_id()]);
131 : *parent_mesh = is_coarsest_grid ? std::nullopt : std::make_optional(mesh);
132 : *observation_id = 0;
133 : } else {
134 : // These items are updated by AMR
135 : (void)parent_id;
136 : (void)child_ids;
137 : (void)parent_mesh;
138 : // These items are only needed during initialization
139 : (void)parent_refinement_levels;
140 : (void)children_refinement_levels;
141 : // Preserve state of observation ID
142 : if constexpr (tt::is_a_v<tuples::TaggedTuple, AmrData...>) {
143 : // h-refinement: copy from the parent
144 : *observation_id =
145 : get<LinearSolver::Tags::ObservationId<OptionsGroup>>(amr_data...);
146 : } else if constexpr (tt::is_a_v<std::unordered_map, AmrData...>) {
147 : // h-coarsening: copy from one of the children (doesn't matter which)
148 : *observation_id = get<LinearSolver::Tags::ObservationId<OptionsGroup>>(
149 : amr_data.begin()->second...);
150 : } else {
151 : (void)observation_id;
152 : }
153 : }
154 : // Initialize volume data output
155 : if (output_volume_data) {
156 : volume_data_for_output->initialize(mesh.number_of_grid_points());
157 : }
158 : }
159 : };
160 :
161 : // These two actions communicate and project the residual from the finer grid to
162 : // the coarser grid, storing it in the `SourceTag` on the coarser grid.
163 : template <typename FieldsTag, typename OptionsGroup,
164 : typename ResidualIsMassiveTag, typename SourceTag>
165 : using SendResidualToCoarserGrid = Actions::SendFieldsToCoarserGrid<
166 : tmpl::list<db::add_tag_prefix<LinearSolver::Tags::Residual, FieldsTag>>,
167 : OptionsGroup, ResidualIsMassiveTag, tmpl::list<SourceTag>>;
168 :
169 : template <size_t Dim, typename FieldsTag, typename OptionsGroup,
170 : typename SourceTag>
171 : using ReceiveResidualFromFinerGrid = Actions::ReceiveFieldsFromFinerGrid<
172 : Dim,
173 : tmpl::list<db::add_tag_prefix<LinearSolver::Tags::Residual, FieldsTag>>,
174 : OptionsGroup, tmpl::list<SourceTag>>;
175 :
176 : // Once the residual from the finer grid has been received and stored in the
177 : // `SourceTag`, this action prepares the pre-smoothing that will determine
178 : // an approximate solution on this grid. The pre-smoother is a separate
179 : // linear solver that runs independently after this action.
180 : template <typename FieldsTag, typename OptionsGroup, typename SourceTag,
181 : bool EnableBottomSolver>
182 : struct PreparePreSmoothing {
183 : private:
184 : using fields_tag = FieldsTag;
185 : using operator_applied_to_fields_tag =
186 : db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo, fields_tag>;
187 : using source_tag = SourceTag;
188 :
189 : public:
190 : using const_global_cache_tags = tmpl::append<
191 : tmpl::list<
192 : LinearSolver::multigrid::Tags::EnablePreSmoothing<OptionsGroup>>,
193 : tmpl::conditional_t<EnableBottomSolver,
194 : tmpl::list<LinearSolver::multigrid::Tags::
195 : UseBottomSolver<OptionsGroup>>,
196 : tmpl::list<>>>;
197 :
198 : template <typename DbTagsList, typename... InboxTags, typename Metavariables,
199 : size_t Dim, typename ActionList, typename ParallelComponent>
200 : static Parallel::iterable_action_return_t apply(
201 : db::DataBox<DbTagsList>& box,
202 : tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
203 : const Parallel::GlobalCache<Metavariables>& /*cache*/,
204 : const ElementId<Dim>& element_id, const ActionList /*meta*/,
205 : const ParallelComponent* const /*meta*/) {
206 : const size_t iteration_id =
207 : db::get<Convergence::Tags::IterationId<OptionsGroup>>(box);
208 : const bool is_coarsest_grid =
209 : not db::get<::amr::Tags::ParentId<Dim>>(box).has_value();
210 : if (UNLIKELY(db::get<logging::Tags::Verbosity<OptionsGroup>>(box) >=
211 : ::Verbosity::Debug)) {
212 : Parallel::printf("%s %s(%zu): Prepare %s\n", element_id,
213 : pretty_type::name<OptionsGroup>(), iteration_id,
214 : (EnableBottomSolver and is_coarsest_grid)
215 : ? "bottom-solver"
216 : : "pre-smoothing");
217 : }
218 :
219 : // On coarser grids the smoother solves for a correction to the finer-grid
220 : // fields, so we set its initial guess to zero. On the finest grid we smooth
221 : // the fields directly, so there's nothing to prepare.
222 : const bool is_finest_grid = db::get<amr::Tags::ChildIds<Dim>>(box).empty();
223 : if (not is_finest_grid) {
224 : db::mutate<fields_tag, operator_applied_to_fields_tag>(
225 : [](const auto fields, const auto operator_applied_to_fields,
226 : const auto& source) {
227 : *fields = make_with_value<typename fields_tag::type>(source, 0.);
228 : // We can set the linear operator applied to the initial fields to
229 : // zero as well, since it's linear
230 : *operator_applied_to_fields =
231 : make_with_value<typename operator_applied_to_fields_tag::type>(
232 : source, 0.);
233 : },
234 : make_not_null(&box), db::get<source_tag>(box));
235 : }
236 :
237 : // Record pre-smoothing initial fields and source
238 : if (db::get<LinearSolver::Tags::OutputVolumeData<OptionsGroup>>(box)) {
239 : db::mutate<Tags::VolumeDataForOutput<OptionsGroup, FieldsTag>>(
240 : [](const auto volume_data, const auto& initial_fields,
241 : const auto& source) {
242 : volume_data->assign_subset(
243 : Variables<db::wrap_tags_in<Tags::PreSmoothingInitial,
244 : typename fields_tag::tags_list>>(
245 : initial_fields));
246 : volume_data->assign_subset(
247 : Variables<db::wrap_tags_in<Tags::PreSmoothingSource,
248 : typename fields_tag::tags_list>>(
249 : source));
250 : },
251 : make_not_null(&box), db::get<fields_tag>(box),
252 : db::get<source_tag>(box));
253 : }
254 :
255 : // Skip pre-smoothing, if requested, or use bottom solver
256 : if constexpr (EnableBottomSolver) {
257 : if (db::get<LinearSolver::multigrid::Tags::UseBottomSolver<OptionsGroup>>(
258 : box) and
259 : is_coarsest_grid) {
260 : const size_t bottom_solver_index =
261 : tmpl::index_of<ActionList, SkipBottomSolver<FieldsTag, OptionsGroup,
262 : SourceTag>>::value +
263 : 1;
264 : return {Parallel::AlgorithmExecution::Continue, bottom_solver_index};
265 : }
266 : }
267 : const size_t first_action_after_pre_smoothing_index = tmpl::index_of<
268 : ActionList,
269 : SkipPostSmoothingAtBottom<FieldsTag, OptionsGroup, SourceTag>>::value;
270 : const size_t this_action_index =
271 : tmpl::index_of<ActionList, PreparePreSmoothing>::value;
272 : return {
273 : Parallel::AlgorithmExecution::Continue,
274 : db::get<
275 : LinearSolver::multigrid::Tags::EnablePreSmoothing<OptionsGroup>>(
276 : box)
277 : ? (this_action_index + 1)
278 : : first_action_after_pre_smoothing_index};
279 : }
280 : };
281 :
282 : // Once pre-smoothing is done, skip the bottom solver that comes next in the
283 : // action list. On the coarsest grid we directly jump to the bottom solver.
284 : template <typename FieldsTag, typename OptionsGroup, typename SourceTag>
285 : struct SkipBottomSolver {
286 : template <typename DbTagsList, typename... InboxTags, typename Metavariables,
287 : size_t Dim, typename ActionList, typename ParallelComponent>
288 : static Parallel::iterable_action_return_t apply(
289 : db::DataBox<DbTagsList>& /*box*/,
290 : const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
291 : const Parallel::GlobalCache<Metavariables>& /*cache*/,
292 : const ElementId<Dim>& /*element_id*/, const ActionList /*meta*/,
293 : const ParallelComponent* const /*meta*/) {
294 : const size_t first_action_after_bottom_solver_index = tmpl::index_of<
295 : ActionList,
296 : SkipPostSmoothingAtBottom<FieldsTag, OptionsGroup, SourceTag>>::value;
297 : return {Parallel::AlgorithmExecution::Continue,
298 : first_action_after_bottom_solver_index};
299 : }
300 : };
301 :
302 : // Once the pre-smoothing is done, we skip the second smoothing step on the
303 : // coarsest grid, i.e. at the "tip" of the V-cycle.
304 : template <typename FieldsTag, typename OptionsGroup, typename SourceTag>
305 : struct SkipPostSmoothingAtBottom {
306 : private:
307 : using fields_tag = FieldsTag;
308 : using residual_tag =
309 : db::add_tag_prefix<LinearSolver::Tags::Residual, fields_tag>;
310 :
311 : public:
312 : using const_global_cache_tags = tmpl::list<
313 : LinearSolver::multigrid::Tags::EnablePostSmoothingAtBottom<OptionsGroup>>;
314 :
315 : template <typename DbTagsList, typename... InboxTags, typename Metavariables,
316 : size_t Dim, typename ActionList, typename ParallelComponent>
317 : static Parallel::iterable_action_return_t apply(
318 : db::DataBox<DbTagsList>& box,
319 : const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
320 : const Parallel::GlobalCache<Metavariables>& /*cache*/,
321 : const ElementId<Dim>& /*element_id*/, const ActionList /*meta*/,
322 : const ParallelComponent* const /*meta*/) {
323 : const bool is_coarsest_grid =
324 : not db::get<amr::Tags::ParentId<Dim>>(box).has_value();
325 :
326 : // Record pre-smoothing result fields and residual
327 : if (db::get<LinearSolver::Tags::OutputVolumeData<OptionsGroup>>(box)) {
328 : db::mutate<Tags::VolumeDataForOutput<OptionsGroup, FieldsTag>>(
329 : [](const auto volume_data, const auto& result_fields,
330 : const auto& residuals) {
331 : volume_data->assign_subset(
332 : Variables<db::wrap_tags_in<Tags::PreSmoothingResult,
333 : typename fields_tag::tags_list>>(
334 : result_fields));
335 : volume_data->assign_subset(
336 : Variables<db::wrap_tags_in<Tags::PreSmoothingResidual,
337 : typename fields_tag::tags_list>>(
338 : residuals));
339 : },
340 : make_not_null(&box), db::get<fields_tag>(box),
341 : db::get<residual_tag>(box));
342 : }
343 :
344 : // Skip post-smoothing on the coarsest grid, if requested
345 : const size_t first_action_after_post_smoothing_index = tmpl::index_of<
346 : ActionList,
347 : SendCorrectionToFinerGrid<FieldsTag, OptionsGroup, SourceTag>>::value;
348 : const size_t post_smoothing_begin_index =
349 : tmpl::index_of<ActionList,
350 : ::Actions::Label<PostSmoothingBeginLabel>>::value +
351 : 1;
352 : const size_t this_action_index =
353 : tmpl::index_of<ActionList, SkipPostSmoothingAtBottom>::value;
354 : return {Parallel::AlgorithmExecution::Continue,
355 : is_coarsest_grid
356 : ? (db::get<LinearSolver::multigrid::Tags::
357 : EnablePostSmoothingAtBottom<OptionsGroup>>(box)
358 : ? post_smoothing_begin_index
359 : : first_action_after_post_smoothing_index)
360 : : (this_action_index + 1)};
361 : }
362 : };
363 :
364 : template <typename FieldsTag>
365 : struct CorrectionInboxTag
366 : : public Parallel::InboxInserters::Value<CorrectionInboxTag<FieldsTag>> {
367 : using temporal_id = size_t;
368 : using type = std::map<temporal_id, typename FieldsTag::type>;
369 : };
370 :
371 : // The next two actions communicate and project the coarse-grid correction, i.e.
372 : // the solution of the post-smoother, to the finer grid. The post-smoother on
373 : // finer grids runs after receiving this coarse-grid correction. Since the
374 : // post-smoother is skipped on the coarsest level, it directly sends the
375 : // solution of the pre-smoother to the finer grid, thus kicking off the
376 : // "ascending" branch of the V-cycle.
377 : template <typename FieldsTag, typename OptionsGroup, typename SourceTag>
378 : struct SendCorrectionToFinerGrid {
379 : private:
380 : using fields_tag = FieldsTag;
381 : using residual_tag =
382 : db::add_tag_prefix<LinearSolver::Tags::Residual, fields_tag>;
383 :
384 : public:
385 : template <typename DbTagsList, typename... InboxTags, typename Metavariables,
386 : size_t Dim, typename ActionList, typename ParallelComponent>
387 : static Parallel::iterable_action_return_t apply(
388 : db::DataBox<DbTagsList>& box,
389 : const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
390 : Parallel::GlobalCache<Metavariables>& cache,
391 : const ElementId<Dim>& element_id, const ActionList /*meta*/,
392 : const ParallelComponent* const /*meta*/) {
393 : const auto& child_ids = db::get<amr::Tags::ChildIds<Dim>>(box);
394 :
395 : // Record post-smoothing result fields and residual
396 : if (db::get<LinearSolver::Tags::OutputVolumeData<OptionsGroup>>(box)) {
397 : db::mutate<Tags::VolumeDataForOutput<OptionsGroup, FieldsTag>>(
398 : [](const auto volume_data, const auto& result_fields,
399 : const auto& residuals) {
400 : volume_data->assign_subset(
401 : Variables<db::wrap_tags_in<Tags::PostSmoothingResult,
402 : typename fields_tag::tags_list>>(
403 : result_fields));
404 : volume_data->assign_subset(
405 : Variables<db::wrap_tags_in<Tags::PostSmoothingResidual,
406 : typename fields_tag::tags_list>>(
407 : residuals));
408 : },
409 : make_not_null(&box), db::get<fields_tag>(box),
410 : db::get<residual_tag>(box));
411 : }
412 :
413 : if (child_ids.empty()) {
414 : return {Parallel::AlgorithmExecution::Continue, std::nullopt};
415 : }
416 :
417 : const size_t iteration_id =
418 : db::get<Convergence::Tags::IterationId<OptionsGroup>>(box);
419 : if (UNLIKELY(db::get<logging::Tags::Verbosity<OptionsGroup>>(box) >=
420 : ::Verbosity::Debug)) {
421 : Parallel::printf("%s %s(%zu): Send correction to children\n", element_id,
422 : pretty_type::name<OptionsGroup>(), iteration_id);
423 : }
424 :
425 : // Send a copy of the correction to all children
426 : auto& receiver_proxy =
427 : Parallel::get_parallel_component<ParallelComponent>(cache);
428 : for (const auto& child_id : child_ids) {
429 : auto coarse_grid_correction = db::get<fields_tag>(box);
430 : Parallel::receive_data<CorrectionInboxTag<FieldsTag>>(
431 : receiver_proxy[child_id], iteration_id,
432 : std::move(coarse_grid_correction));
433 : }
434 : return {Parallel::AlgorithmExecution::Continue, std::nullopt};
435 : }
436 : };
437 :
438 : template <size_t Dim, typename FieldsTag, typename OptionsGroup,
439 : typename SourceTag>
440 : struct ReceiveCorrectionFromCoarserGrid {
441 : private:
442 : using fields_tag = FieldsTag;
443 : using source_tag = SourceTag;
444 :
445 : public:
446 : using inbox_tags = tmpl::list<CorrectionInboxTag<FieldsTag>>;
447 :
448 : template <typename DbTagsList, typename... InboxTags, typename Metavariables,
449 : typename ActionList, typename ParallelComponent>
450 : static Parallel::iterable_action_return_t apply(
451 : db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,
452 : const Parallel::GlobalCache<Metavariables>& /*cache*/,
453 : const ElementId<Dim>& element_id, const ActionList /*meta*/,
454 : const ParallelComponent* const /*meta*/) {
455 : const auto& parent_id = db::get<amr::Tags::ParentId<Dim>>(box);
456 : // We should always have a `parent_id` at this point because we skip this
457 : // part of the algorithm on the coarsest grid with the
458 : // `SkipPostSmoothingAtBottom` action
459 : ASSERT(parent_id.has_value(),
460 : "Trying to receive data from parent but no parent is set on element "
461 : << element_id << ".");
462 : const size_t iteration_id =
463 : db::get<Convergence::Tags::IterationId<OptionsGroup>>(box);
464 :
465 : // Wait for data from coarser grid
466 : auto& inbox = tuples::get<CorrectionInboxTag<FieldsTag>>(inboxes);
467 : if (inbox.find(iteration_id) == inbox.end()) {
468 : return {Parallel::AlgorithmExecution::Retry, std::nullopt};
469 : }
470 : auto parent_correction = std::move(inbox.extract(iteration_id).mapped());
471 :
472 : if (UNLIKELY(db::get<logging::Tags::Verbosity<OptionsGroup>>(box) >=
473 : ::Verbosity::Debug)) {
474 : Parallel::printf("%s %s(%zu): Prolongate correction from parent\n",
475 : element_id, pretty_type::name<OptionsGroup>(),
476 : iteration_id);
477 : }
478 :
479 : // Apply prolongation operator
480 : const auto& mesh = db::get<domain::Tags::Mesh<Dim>>(box);
481 : const auto& parent_mesh = db::get<amr::Tags::ParentMesh<Dim>>(box);
482 : ASSERT(
483 : parent_mesh.has_value(),
484 : "Should have a parent mesh, because a parent ID is set. This element: "
485 : << element_id << ", parent element: " << *parent_id);
486 : const auto child_size =
487 : domain::child_size(element_id.segment_ids(), parent_id->segment_ids());
488 : const auto prolongated_parent_correction =
489 : [&parent_correction, &parent_mesh, &mesh, &child_size]() {
490 : if (Spectral::needs_projection(*parent_mesh, mesh, child_size)) {
491 : return Spectral::project(
492 : parent_correction, *parent_mesh, mesh,
493 : make_array<Dim>(Spectral::SegmentSize::Full), child_size);
494 : } else {
495 : return std::move(parent_correction);
496 : }
497 : }();
498 :
499 : // Add correction to the solution on this grid
500 : db::mutate<fields_tag>(
501 : [&prolongated_parent_correction](const auto fields) {
502 : *fields += prolongated_parent_correction;
503 : },
504 : make_not_null(&box));
505 :
506 : // Record post-smoothing initial fields and source
507 : if (db::get<LinearSolver::Tags::OutputVolumeData<OptionsGroup>>(box)) {
508 : db::mutate<Tags::VolumeDataForOutput<OptionsGroup, FieldsTag>>(
509 : [](const auto volume_data, const auto& initial_fields,
510 : const auto& source) {
511 : volume_data->assign_subset(
512 : Variables<db::wrap_tags_in<Tags::PostSmoothingInitial,
513 : typename fields_tag::tags_list>>(
514 : initial_fields));
515 : volume_data->assign_subset(
516 : Variables<db::wrap_tags_in<Tags::PostSmoothingSource,
517 : typename fields_tag::tags_list>>(
518 : source));
519 : },
520 : make_not_null(&box), db::get<fields_tag>(box),
521 : db::get<source_tag>(box));
522 : }
523 :
524 : return {Parallel::AlgorithmExecution::Continue, std::nullopt};
525 : }
526 : };
527 :
528 : } // namespace LinearSolver::multigrid::detail
|