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 <string>
8 :
9 : #include "DataStructures/DataBox/PrefixHelpers.hpp"
10 : #include "DataStructures/DataBox/Prefixes.hpp"
11 : #include "DataStructures/Variables.hpp"
12 : #include "Domain/Tags/Faces.hpp"
13 : #include "Elliptic/Actions/InitializeAnalyticSolution.hpp"
14 : #include "Elliptic/Actions/InitializeFields.hpp"
15 : #include "Elliptic/Actions/InitializeFixedSources.hpp"
16 : #include "Elliptic/Actions/RunEventsAndTriggers.hpp"
17 : #include "Elliptic/Amr/Actions.hpp"
18 : #include "Elliptic/BoundaryConditions/Tags/BoundaryFields.hpp"
19 : #include "Elliptic/DiscontinuousGalerkin/Actions/ApplyOperator.hpp"
20 : #include "Elliptic/DiscontinuousGalerkin/Actions/InitializeDomain.hpp"
21 : #include "Elliptic/DiscontinuousGalerkin/SubdomainOperator/InitializeSubdomain.hpp"
22 : #include "Elliptic/DiscontinuousGalerkin/SubdomainOperator/SubdomainOperator.hpp"
23 : #include "Elliptic/DiscontinuousGalerkin/Tags.hpp"
24 : #include "Elliptic/Protocols/FirstOrderSystem.hpp"
25 : #include "Elliptic/SubdomainPreconditioners/MinusLaplacian.hpp"
26 : #include "Elliptic/Systems/GetSourcesComputer.hpp"
27 : #include "Elliptic/Tags.hpp"
28 : #include "IO/Observer/Helpers.hpp"
29 : #include "NumericalAlgorithms/Convergence/Tags.hpp"
30 : #include "Options/String.hpp"
31 : #include "Parallel/PhaseControl/ExecutePhaseChange.hpp"
32 : #include "ParallelAlgorithms/Actions/AddComputeTags.hpp"
33 : #include "ParallelAlgorithms/Actions/InitializeItems.hpp"
34 : #include "ParallelAlgorithms/Actions/MutateApply.hpp"
35 : #include "ParallelAlgorithms/Actions/RandomizeVariables.hpp"
36 : #include "ParallelAlgorithms/Amr/Actions/Component.hpp"
37 : #include "ParallelAlgorithms/Amr/Actions/ElementsRegistration.hpp"
38 : #include "ParallelAlgorithms/Amr/Actions/Initialize.hpp"
39 : #include "ParallelAlgorithms/Amr/Projectors/DefaultInitialize.hpp"
40 : #include "ParallelAlgorithms/Amr/Projectors/Variables.hpp"
41 : #include "ParallelAlgorithms/Amr/Tags.hpp"
42 : #include "ParallelAlgorithms/LinearSolver/Actions/BuildMatrix.hpp"
43 : #include "ParallelAlgorithms/LinearSolver/Actions/MakeIdentityIfSkipped.hpp"
44 : #include "ParallelAlgorithms/LinearSolver/Gmres/Gmres.hpp"
45 : #include "ParallelAlgorithms/LinearSolver/Multigrid/Actions/RestrictFields.hpp"
46 : #include "ParallelAlgorithms/LinearSolver/Multigrid/Multigrid.hpp"
47 : #include "ParallelAlgorithms/LinearSolver/Schwarz/Actions/CommunicateOverlapFields.hpp"
48 : #include "ParallelAlgorithms/LinearSolver/Schwarz/Actions/ResetSubdomainSolver.hpp"
49 : #include "ParallelAlgorithms/LinearSolver/Schwarz/Schwarz.hpp"
50 : #include "ParallelAlgorithms/LinearSolver/Tags.hpp"
51 : #include "ParallelAlgorithms/NonlinearSolver/NewtonRaphson/NewtonRaphson.hpp"
52 : #include "ParallelAlgorithms/NonlinearSolver/Tags.hpp"
53 : #include "PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp"
54 : #include "PointwiseFunctions/InitialDataUtilities/Background.hpp"
55 : #include "PointwiseFunctions/InitialDataUtilities/InitialGuess.hpp"
56 : #include "Utilities/ProtocolHelpers.hpp"
57 : #include "Utilities/TMPL.hpp"
58 :
59 : /// Items related to composing nonlinear elliptic solver executables
60 : namespace elliptic {
61 :
62 : /// Option tags for nonlinear elliptic solver executables
63 : namespace OptionTags {
64 :
65 0 : struct NonlinearSolverGroup {
66 0 : static std::string name() { return "NonlinearSolver"; }
67 0 : static constexpr Options::String help = "The iterative nonlinear solver";
68 : };
69 0 : struct NewtonRaphsonGroup {
70 0 : static std::string name() { return "NewtonRaphson"; }
71 0 : static constexpr Options::String help =
72 : "Options for the Newton-Raphson nonlinear solver";
73 0 : using group = NonlinearSolverGroup;
74 : };
75 0 : struct LinearSolverGroup {
76 0 : static std::string name() { return "LinearSolver"; }
77 0 : static constexpr Options::String help =
78 : "The iterative Krylov-subspace linear solver";
79 : };
80 0 : struct GmresGroup {
81 0 : static std::string name() { return "Gmres"; }
82 0 : static constexpr Options::String help = "Options for the GMRES linear solver";
83 0 : using group = LinearSolverGroup;
84 : };
85 0 : struct SchwarzSmootherGroup {
86 0 : static std::string name() { return "SchwarzSmoother"; }
87 0 : static constexpr Options::String help = "Options for the Schwarz smoother";
88 0 : using group = LinearSolverGroup;
89 : };
90 0 : struct MultigridGroup {
91 0 : static std::string name() { return "Multigrid"; }
92 0 : static constexpr Options::String help = "Options for the multigrid";
93 0 : using group = LinearSolverGroup;
94 : };
95 :
96 : } // namespace OptionTags
97 :
98 : /*!
99 : * \brief A complete nonlinear elliptic solver stack. Use to compose an
100 : * executable.
101 : *
102 : * The elliptic solver stack is described in detail in \cite Vu2021coj.
103 : *
104 : * Uses `Metavariables` to instantiate parallel components.
105 : */
106 : template <typename Metavariables, size_t Dim, typename System>
107 1 : struct Solver {
108 0 : static constexpr size_t volume_dim = Dim;
109 0 : using system = System;
110 : static_assert(
111 : tt::assert_conforms_to_v<system, elliptic::protocols::FirstOrderSystem>);
112 0 : static constexpr bool is_linear =
113 : std::is_same_v<elliptic::get_sources_computer<system, true>,
114 : typename system::sources_computer>;
115 :
116 0 : using background_tag =
117 : elliptic::Tags::Background<elliptic::analytic_data::Background>;
118 0 : using initial_guess_tag =
119 : elliptic::Tags::InitialGuess<elliptic::analytic_data::InitialGuess>;
120 :
121 : /// These are the fields we solve for
122 1 : using fields_tag = ::Tags::Variables<typename system::primal_fields>;
123 : /// These are the fluxes corresponding to the fields, i.e. essentially their
124 : /// first derivatives. These are background fields for the linearized sources.
125 1 : using fluxes_tag = ::Tags::Variables<typename system::primal_fluxes>;
126 : /// These are the fixed sources, i.e. the RHS of the equations
127 1 : using fixed_sources_tag = db::add_tag_prefix<::Tags::FixedSource, fields_tag>;
128 0 : using operator_applied_to_fields_tag = tmpl::conditional_t<
129 : is_linear,
130 : // This is the linear operator applied to the fields. We only use it to
131 : // apply the operator to the initial guess, so an optimization would be to
132 : // re-use the `operator_applied_to_vars_tag` below. This optimization
133 : // needs a few minor changes to the parallel linear solver algorithm.
134 : db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo, fields_tag>,
135 : db::add_tag_prefix<NonlinearSolver::Tags::OperatorAppliedTo, fields_tag>>;
136 :
137 : // For AMR iterations
138 0 : using amr_iteration_id =
139 : Convergence::Tags::IterationId<::amr::OptionTags::AmrGroup>;
140 :
141 : /// The nonlinear solver algorithm
142 1 : using nonlinear_solver = NonlinearSolver::newton_raphson::NewtonRaphson<
143 : Metavariables, fields_tag, OptionTags::NewtonRaphsonGroup,
144 : fixed_sources_tag, ::amr::Tags::IsFinestGrid>;
145 0 : using nonlinear_solver_iteration_id =
146 : Convergence::Tags::IterationId<typename nonlinear_solver::options_group>;
147 :
148 : /// The linear solver algorithm. We use GMRES since the operator is not
149 : /// necessarily symmetric. Using CG here would be an optimization for
150 : /// symmetric problems.
151 1 : using linear_solver = LinearSolver::gmres::Gmres<
152 : Metavariables,
153 : tmpl::conditional_t<is_linear, fields_tag,
154 : typename nonlinear_solver::linear_solver_fields_tag>,
155 : OptionTags::GmresGroup, true,
156 : tmpl::conditional_t<is_linear, fixed_sources_tag,
157 : typename nonlinear_solver::linear_solver_source_tag>,
158 : ::amr::Tags::IsFinestGrid>;
159 0 : using linear_solver_iteration_id =
160 : Convergence::Tags::IterationId<typename linear_solver::options_group>;
161 :
162 : /// Precondition each linear solver iteration with a multigrid V-cycle
163 1 : using multigrid = LinearSolver::multigrid::Multigrid<
164 : Metavariables, volume_dim, typename linear_solver::operand_tag,
165 : OptionTags::MultigridGroup, elliptic::dg::Tags::Massive,
166 : typename linear_solver::preconditioner_source_tag>;
167 :
168 : /// Smooth each multigrid level with a number of Schwarz smoothing steps
169 1 : using subdomain_operator =
170 : elliptic::dg::subdomain_operator::SubdomainOperator<
171 : system, OptionTags::SchwarzSmootherGroup>;
172 0 : using subdomain_preconditioners = tmpl::list<
173 : elliptic::subdomain_preconditioners::Registrars::MinusLaplacian<
174 : volume_dim, OptionTags::SchwarzSmootherGroup>>;
175 0 : using schwarz_smoother = LinearSolver::Schwarz::Schwarz<
176 : typename multigrid::smooth_fields_tag, OptionTags::SchwarzSmootherGroup,
177 : subdomain_operator, subdomain_preconditioners,
178 : typename multigrid::smooth_source_tag, ::amr::Tags::GridIndex>;
179 :
180 : /// For the GMRES linear solver we need to apply the DG operator to its
181 : /// internal "operand" in every iteration of the algorithm.
182 1 : using vars_tag = typename linear_solver::operand_tag;
183 0 : using operator_applied_to_vars_tag =
184 : db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo, vars_tag>;
185 : /// The correction fluxes can be stored in an arbitrary tag. We don't need to
186 : /// access them anywhere, they're just a memory buffer for the linearized
187 : /// operator.
188 1 : using fluxes_vars_tag =
189 : db::add_tag_prefix<NonlinearSolver::Tags::Correction, fluxes_tag>;
190 :
191 : /// Fields that may be observed to monitor the state of the solver
192 1 : using observe_fields = tmpl::conditional_t<
193 : is_linear,
194 : tmpl::append<
195 : typename fixed_sources_tag::tags_list,
196 : typename db::add_tag_prefix<LinearSolver::Tags::Operand,
197 : fields_tag>::tags_list,
198 : typename db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo,
199 : fields_tag>::tags_list>,
200 : tmpl::append<
201 : typename fixed_sources_tag::tags_list,
202 : typename nonlinear_solver::linear_solver_fields_tag::tags_list,
203 : typename nonlinear_solver::linear_solver_source_tag::tags_list>>;
204 :
205 : /// Collect all reduction tags for observers
206 1 : using observed_reduction_data_tags =
207 : observers::collect_reduction_data_tags<tmpl::flatten<tmpl::list<
208 : tmpl::conditional_t<is_linear, tmpl::list<>, nonlinear_solver>,
209 : linear_solver, multigrid, schwarz_smoother>>>;
210 :
211 : template <bool Linearized>
212 0 : using dg_operator = elliptic::dg::Actions::DgOperator<
213 : system, Linearized, tmpl::conditional_t<Linearized, vars_tag, fields_tag>,
214 : tmpl::conditional_t<Linearized, fluxes_vars_tag, fluxes_tag>,
215 : tmpl::conditional_t<Linearized, operator_applied_to_vars_tag,
216 : operator_applied_to_fields_tag>>;
217 :
218 0 : using build_matrix = LinearSolver::Actions::BuildMatrix<
219 : typename multigrid::smooth_fields_tag,
220 : typename multigrid::smooth_source_tag, vars_tag,
221 : operator_applied_to_vars_tag,
222 : domain::Tags::Coordinates<volume_dim, Frame::Inertial>,
223 : ::amr::Tags::GridIndex>;
224 :
225 0 : using build_matrix_actions = typename build_matrix::template actions<
226 : typename dg_operator<true>::apply_actions>;
227 :
228 : // For labeling the yaml option for RandomizeVariables
229 0 : struct RandomizeInitialGuess {};
230 :
231 0 : using init_analytic_solution_action =
232 : elliptic::Actions::InitializeOptionalAnalyticSolution<
233 : volume_dim, background_tag,
234 : tmpl::append<typename system::primal_fields,
235 : typename system::primal_fluxes>,
236 : elliptic::analytic_data::AnalyticSolution>;
237 :
238 0 : using initialization_actions = tmpl::list<
239 : elliptic::dg::Actions::InitializeDomain<volume_dim>,
240 : tmpl::conditional_t<is_linear, tmpl::list<>,
241 : typename nonlinear_solver::initialize_element>,
242 : typename linear_solver::initialize_element,
243 : typename multigrid::initialize_element,
244 : typename schwarz_smoother::initialize_element,
245 : Initialization::Actions::InitializeItems<
246 : ::amr::Initialization::Initialize<volume_dim, Metavariables>,
247 : elliptic::amr::Actions::Initialize>,
248 : elliptic::Actions::InitializeFields<system, initial_guess_tag>,
249 : ::Actions::RandomizeVariables<fields_tag, RandomizeInitialGuess>,
250 : init_analytic_solution_action,
251 : elliptic::dg::Actions::initialize_operator<system, background_tag>,
252 : // Actions below may use faces and normals
253 : elliptic::Actions::InitializeFixedSources<system, background_tag>,
254 : tmpl::conditional_t<is_linear,
255 : tmpl::list<::Actions::MutateApply<
256 : elliptic::dg::Actions::
257 : ImposeInhomogeneousBoundaryConditionsOnSource<
258 : system, fixed_sources_tag>>>,
259 : tmpl::list<>>,
260 : tmpl::conditional_t<
261 : is_linear, tmpl::list<>,
262 : ::Initialization::Actions::AddComputeTags<tmpl::list<
263 : // For linearized boundary conditions
264 : elliptic::Tags::BoundaryFieldsCompute<volume_dim, fields_tag>,
265 : elliptic::Tags::BoundaryFluxesCompute<volume_dim, fields_tag,
266 : fluxes_tag>>>>>;
267 :
268 0 : using register_actions = tmpl::flatten<tmpl::list<
269 : ::amr::Actions::RegisterElement,
270 : tmpl::conditional_t<is_linear, tmpl::list<>,
271 : typename nonlinear_solver::register_element>,
272 : typename multigrid::register_element,
273 : typename schwarz_smoother::register_element,
274 : typename build_matrix::register_actions>>;
275 :
276 : template <typename Label>
277 0 : using smooth_actions = typename schwarz_smoother::template solve<
278 : typename dg_operator<true>::apply_actions, Label>;
279 :
280 : // These tags are communicated on subdomain overlaps to initialize the
281 : // subdomain geometry. AMR updates these tags, so we have to communicate them
282 : // after each AMR step.
283 0 : using subdomain_init_tags =
284 : tmpl::list<domain::Tags::Mesh<volume_dim>,
285 : domain::Tags::Element<volume_dim>,
286 : domain::Tags::NeighborMesh<volume_dim>>;
287 :
288 : /// This data needs to be communicated on subdomain overlap regions
289 1 : using communicated_overlap_tags = tmpl::flatten<tmpl::list<
290 : // For linearized sources
291 : fields_tag, fluxes_tag,
292 : // For linearized boundary conditions
293 : domain::make_faces_tags<volume_dim, typename system::primal_fields>,
294 : domain::make_faces_tags<
295 : volume_dim, db::wrap_tags_in<::Tags::NormalDotFlux,
296 : typename system::primal_fields>>>>;
297 :
298 0 : using init_subdomain_action =
299 : elliptic::dg::subdomain_operator::Actions::InitializeSubdomain<
300 : system, background_tag, typename schwarz_smoother::options_group,
301 : false>;
302 :
303 : template <typename StepActions>
304 0 : using linear_solve_actions = typename linear_solver::template solve<
305 : tmpl::list<
306 : // Multigrid preconditioning
307 : typename multigrid::template solve<
308 : typename dg_operator<true>::apply_actions,
309 : // Schwarz smoothing on each multigrid level
310 : smooth_actions<LinearSolver::multigrid::VcycleDownLabel>,
311 : smooth_actions<LinearSolver::multigrid::VcycleUpLabel>,
312 : build_matrix_actions>,
313 : // Support disabling the preconditioner
314 : ::LinearSolver::Actions::make_identity_if_skipped<
315 : multigrid, typename dg_operator<true>::apply_actions>>,
316 : StepActions>;
317 :
318 : template <typename StepActions>
319 0 : using nonlinear_solve_actions = typename nonlinear_solver::template solve<
320 : typename dg_operator<false>::apply_actions,
321 : tmpl::list<
322 : // Transfer data down the multigrid hierachy
323 : LinearSolver::multigrid::Actions::ReceiveFieldsFromFinerGrid<
324 : volume_dim, tmpl::list<fields_tag, fluxes_tag>,
325 : typename multigrid::options_group>,
326 : LinearSolver::multigrid::Actions::SendFieldsToCoarserGrid<
327 : tmpl::list<fields_tag, fluxes_tag>,
328 : typename multigrid::options_group, void>,
329 : // Communicate data on subdomain overlap regions
330 : LinearSolver::Schwarz::Actions::SendOverlapFields<
331 : communicated_overlap_tags,
332 : typename schwarz_smoother::options_group, false,
333 : nonlinear_solver_iteration_id>,
334 : LinearSolver::Schwarz::Actions::ReceiveOverlapFields<
335 : volume_dim, communicated_overlap_tags,
336 : typename schwarz_smoother::options_group, false,
337 : nonlinear_solver_iteration_id>,
338 : // Reset Schwarz subdomain solver
339 : LinearSolver::Schwarz::Actions::ResetSubdomainSolver<
340 : typename schwarz_smoother::subdomain_solver,
341 : typename schwarz_smoother::options_group>,
342 : // Reset explicitly built matrix
343 : typename build_matrix::reset_actions,
344 : // Linear solve for correction
345 : linear_solve_actions<tmpl::list<>>>,
346 : StepActions>;
347 :
348 : template <typename StepActions>
349 0 : using solve_actions = tmpl::flatten<tmpl::list<
350 : // Observe initial state
351 : elliptic::Actions::RunEventsAndTriggers<amr_iteration_id>,
352 : // Stop AMR iterations if complete
353 : elliptic::amr::Actions::StopAmr,
354 : // Run AMR or other phase changes if requested
355 : PhaseControl::Actions::ExecutePhaseChange, StepActions,
356 : // Increment AMR iteration ID
357 : elliptic::amr::Actions::IncrementIterationId,
358 : // Communicate subdomain geometry and initialize subdomain to account for
359 : // domain changes
360 : LinearSolver::Schwarz::Actions::SendOverlapFields<
361 : subdomain_init_tags, typename schwarz_smoother::options_group, false,
362 : amr_iteration_id>,
363 : LinearSolver::Schwarz::Actions::ReceiveOverlapFields<
364 : volume_dim, subdomain_init_tags,
365 : typename schwarz_smoother::options_group, false, amr_iteration_id>,
366 : init_subdomain_action,
367 : // Linear or nonlinear solve
368 : tmpl::conditional_t<is_linear,
369 : // Linear solve
370 : tmpl::list<
371 : // Apply the DG operator to the initial guess
372 : typename elliptic::dg::Actions::DgOperator<
373 : system, true, fields_tag, fluxes_vars_tag,
374 : operator_applied_to_fields_tag, vars_tag,
375 : fluxes_vars_tag>::apply_actions,
376 : // Krylov solve
377 : linear_solve_actions<tmpl::list<>>>,
378 : // Nonlinear solve
379 : nonlinear_solve_actions<tmpl::list<>>>>>;
380 :
381 : template <typename Tag>
382 0 : using overlaps_tag =
383 : LinearSolver::Schwarz::Tags::Overlaps<Tag, volume_dim,
384 : OptionTags::SchwarzSmootherGroup>;
385 :
386 0 : using amr_projectors = tmpl::flatten<tmpl::list<
387 : elliptic::dg::ProjectGeometry<volume_dim>,
388 : tmpl::conditional_t<is_linear, tmpl::list<>,
389 : typename nonlinear_solver::amr_projectors>,
390 : typename linear_solver::amr_projectors,
391 : typename multigrid::amr_projectors,
392 : typename schwarz_smoother::amr_projectors,
393 : typename build_matrix::amr_projectors,
394 : ::amr::projectors::DefaultInitialize<tmpl::append<
395 : tmpl::list<domain::Tags::InitialExtents<volume_dim>,
396 : domain::Tags::InitialRefinementLevels<volume_dim>>,
397 : // Tags communicated on subdomain overlaps. No need to project
398 : // these during AMR because they will be communicated.
399 : db::wrap_tags_in<
400 : overlaps_tag,
401 : tmpl::append<subdomain_init_tags,
402 : tmpl::conditional_t<is_linear, tmpl::list<>,
403 : communicated_overlap_tags>>>,
404 : // Tags initialized on subdomains. No need to project these during
405 : // AMR because they will get re-initialized after communication.
406 : typename init_subdomain_action::simple_tags>>,
407 : ::amr::projectors::ProjectVariables<volume_dim, fields_tag>,
408 : init_analytic_solution_action,
409 : elliptic::dg::Actions::amr_projectors<system, background_tag>,
410 : typename dg_operator<true>::amr_projectors,
411 : tmpl::conditional_t<is_linear, tmpl::list<>,
412 : typename dg_operator<false>::amr_projectors>,
413 : // Actions below may use faces and normals
414 : elliptic::Actions::InitializeFixedSources<system, background_tag>,
415 : tmpl::conditional_t<
416 : is_linear,
417 : tmpl::list<elliptic::dg::Actions::
418 : ImposeInhomogeneousBoundaryConditionsOnSource<
419 : system, fixed_sources_tag>>,
420 : tmpl::list<>>,
421 : elliptic::amr::Actions::Initialize>>;
422 :
423 0 : using component_list = tmpl::flatten<
424 : tmpl::list<tmpl::conditional_t<is_linear, tmpl::list<>,
425 : typename nonlinear_solver::component_list>,
426 : typename linear_solver::component_list,
427 : typename multigrid::component_list,
428 : typename schwarz_smoother::component_list,
429 : typename build_matrix::template component_list<Metavariables>,
430 : ::amr::Component<Metavariables>>>;
431 : };
432 :
433 : } // namespace elliptic
|