Skip to content

Commit a5f97e6

Browse files
authored
Adjust FMG approximation - Skip costly multigrid cycles on finest level (#185)
1 parent 11860a9 commit a5f97e6

File tree

4 files changed

+72
-80
lines changed

4 files changed

+72
-80
lines changed

include/GMGPolar/igmgpolar.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ class IGMGPolar
262262

263263
/* --------------- */
264264
/* Solve Functions */
265-
void initializeSolution();
265+
void fullMultigridApproximation(MultigridCycleType FMG_cycle, int FMG_iterations);
266266
double residualNorm(const ResidualNormType& norm_type, const Level& level, ConstVector<double> residual) const;
267267
void evaluateExactError(Level& level, const ExactSolution& exact_solution);
268268
void updateResidualNorms(Level& level, int iteration, double& initial_residual_norm, double& current_residual_norm,

include/GMGPolar/solver.h

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,31 @@ void IGMGPolar::solve(const BoundaryConditions& boundary_conditions, const Sourc
4141
/* ---------------------------- */
4242
auto start_initial_approximation = std::chrono::high_resolution_clock::now();
4343

44-
initializeSolution();
44+
if (!FMG_) {
45+
// Assign zero initial guess if not using FMG
46+
assign(levels_[0].solution(), 0.0);
47+
}
48+
else {
49+
// Compute an initial approximation to the discrete system
50+
//
51+
// A * levels_[0].solution() = levels_[0].rhs()
52+
//
53+
// using the Full Multigrid (FMG) algorithm.
54+
//
55+
// Prerequisite:
56+
// The right-hand side must already be properly initialized on all levels,
57+
// i.e., constructed on the finest level and transferred to coarser levels
58+
// via injection and discretization. This ensures consistency of the coarse-
59+
// grid operators and guarantees correctness of the multigrid hierarchy.
60+
//
61+
// The FMG algorithm performs an exact solve on the coarsest grid and then
62+
// successively prolongates the solution to finer grids, applying multigrid
63+
// cycles on each level to reduce the error. This produces a high-quality
64+
// initial guess on the finest level, typically reducing the error to the
65+
// order of the discretization error and significantly accelerating convergence
66+
// of the subsequent solve phase.
67+
fullMultigridApproximation(FMG_cycle_, FMG_iterations_);
68+
}
4569

4670
auto end_initial_approximation = std::chrono::high_resolution_clock::now();
4771
t_solve_initial_approximation_ =

src/GMGPolar/solver.cpp

Lines changed: 39 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,48 @@
11
#include "../../include/GMGPolar/gmgpolar.h"
22

33
// =============================================================================
4-
// Solution Initialization
4+
// Full Multigrid Approximation
55
// =============================================================================
66

7-
void IGMGPolar::initializeSolution()
7+
void IGMGPolar::fullMultigridApproximation(MultigridCycleType FMG_cycle, int FMG_iterations)
88
{
9-
if (!FMG_) {
10-
int start_level_depth = 0;
11-
Level& level = levels_[start_level_depth];
12-
assign(level.solution(), 0.0); // Assign zero initial guess if not using FMG
13-
}
14-
else {
15-
// Start from the coarsest level
16-
int coarsest_depth = number_of_levels_ - 1;
17-
Level& coarsest_level = levels_[coarsest_depth];
18-
19-
// Solve directly on the coarsest level
20-
Kokkos::deep_copy(coarsest_level.solution(), coarsest_level.rhs());
21-
coarsest_level.directSolveInPlace(coarsest_level.solution()); // Direct solve on coarsest grid
22-
23-
// Prolongate the solution from the coarsest level up to the finest, while applying Multigrid Cycles on each level
24-
for (int depth = coarsest_depth; depth > 0; --depth) {
25-
Level& coarse_level = levels_[depth]; // Current coarse level
26-
Level& fine_level = levels_[depth - 1]; // Next finer level
27-
28-
// The bi-cubic FMG interpolation is of higher order
29-
FMGInterpolation(coarse_level.level_depth(), fine_level.solution(), coarse_level.solution());
30-
31-
// Apply some FMG iterations
32-
for (int i = 0; i < FMG_iterations_; i++) {
33-
if (fine_level.level_depth() == 0 && (extrapolation_ != ExtrapolationType::NONE)) {
34-
switch (FMG_cycle_) {
35-
case MultigridCycleType::V_CYCLE:
36-
extrapolated_multigrid_V_Cycle(fine_level.level_depth(), fine_level.solution(),
37-
fine_level.rhs(), fine_level.residual());
38-
break;
39-
40-
case MultigridCycleType::W_CYCLE:
41-
extrapolated_multigrid_W_Cycle(fine_level.level_depth(), fine_level.solution(),
42-
fine_level.rhs(), fine_level.residual());
43-
break;
44-
45-
case MultigridCycleType::F_CYCLE:
46-
extrapolated_multigrid_F_Cycle(fine_level.level_depth(), fine_level.solution(),
47-
fine_level.rhs(), fine_level.residual());
48-
break;
49-
50-
default:
51-
std::cerr << "Error: Unknown multigrid cycle type!" << std::endl;
52-
throw std::runtime_error("Invalid multigrid cycle type encountered.");
53-
break;
54-
}
55-
}
56-
else {
57-
switch (FMG_cycle_) {
58-
case MultigridCycleType::V_CYCLE:
59-
multigrid_V_Cycle(fine_level.level_depth(), fine_level.solution(), fine_level.rhs(),
60-
fine_level.residual());
61-
break;
62-
63-
case MultigridCycleType::W_CYCLE:
64-
multigrid_W_Cycle(fine_level.level_depth(), fine_level.solution(), fine_level.rhs(),
65-
fine_level.residual());
66-
break;
67-
68-
case MultigridCycleType::F_CYCLE:
69-
multigrid_F_Cycle(fine_level.level_depth(), fine_level.solution(), fine_level.rhs(),
70-
fine_level.residual());
71-
break;
72-
73-
default:
74-
std::cerr << "Error: Unknown multigrid cycle type!" << std::endl;
75-
throw std::runtime_error("Invalid multigrid cycle type encountered.");
76-
break;
77-
}
9+
// Start from the coarsest level
10+
int coarsest_depth = number_of_levels_ - 1;
11+
Level& coarsest_level = levels_[coarsest_depth];
12+
13+
// Solve directly on the coarsest level
14+
Kokkos::deep_copy(coarsest_level.solution(), coarsest_level.rhs());
15+
coarsest_level.directSolveInPlace(coarsest_level.solution()); // Direct solve on coarsest grid
16+
17+
// Prolongate the solution from the coarsest level up to the finest, while applying Multigrid Cycles on each level
18+
for (int depth = coarsest_depth; depth > 0; --depth) {
19+
Level& coarse_level = levels_[depth]; // Current coarse level
20+
Level& fine_level = levels_[depth - 1]; // Next finer level
21+
22+
// The bi-cubic FMG interpolation is of higher order
23+
FMGInterpolation(coarse_level.level_depth(), fine_level.solution(), coarse_level.solution());
24+
25+
// Apply some FMG iterations, except on the finest level,
26+
// where the interpolated solution is sufficintly accurate as an initial guess
27+
if (fine_level.level_depth() > 0) {
28+
for (int i = 0; i < FMG_iterations; i++) {
29+
switch (FMG_cycle) {
30+
case MultigridCycleType::V_CYCLE:
31+
multigrid_V_Cycle(fine_level.level_depth(), fine_level.solution(), fine_level.rhs(),
32+
fine_level.residual());
33+
break;
34+
case MultigridCycleType::W_CYCLE:
35+
multigrid_W_Cycle(fine_level.level_depth(), fine_level.solution(), fine_level.rhs(),
36+
fine_level.residual());
37+
break;
38+
case MultigridCycleType::F_CYCLE:
39+
multigrid_F_Cycle(fine_level.level_depth(), fine_level.solution(), fine_level.rhs(),
40+
fine_level.residual());
41+
break;
42+
default:
43+
std::cerr << "Error: Unknown multigrid cycle type!" << std::endl;
44+
throw std::runtime_error("Invalid multigrid cycle type encountered.");
45+
break;
7846
}
7947
}
8048
}

tests/GMGPolar/solve_tests.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ using gmgpolar_test_suite = testing::Types<
190190
std::integral_constant<ResidualNormType, ResidualNormType::EUCLIDEAN>, // residualNormType
191191
std::integral_constant<double, 1e-8>, // absoluteTolerance
192192
std::integral_constant<double, 1e-8>, // relativeTolerance
193-
std::integral_constant<int, 26>, // expected_iterations
193+
std::integral_constant<int, 27>, // expected_iterations
194194
std::integral_constant<double, 2e-6>, // expected_l2_error
195195
std::integral_constant<double, 9e-6>, // expected_inf_error
196196
std::integral_constant<double, 0.7> // expected_residual_reduction
@@ -227,7 +227,7 @@ using gmgpolar_test_suite = testing::Types<
227227
std::integral_constant<ResidualNormType, ResidualNormType::EUCLIDEAN>, // residualNormType
228228
std::integral_constant<double, 1e-8>, // absoluteTolerance
229229
std::integral_constant<double, 1e-8>, // relativeTolerance
230-
std::integral_constant<int, 26>, // expected_iterations
230+
std::integral_constant<int, 27>, // expected_iterations
231231
std::integral_constant<double, 2e-6>, // expected_l2_error
232232
std::integral_constant<double, 9e-6>, // expected_inf_error
233233
std::integral_constant<double, 0.7> // expected_residual_reduction
@@ -264,7 +264,7 @@ using gmgpolar_test_suite = testing::Types<
264264
std::integral_constant<ResidualNormType, ResidualNormType::EUCLIDEAN>, // residualNormType
265265
std::integral_constant<double, 1e-8>, // absoluteTolerance
266266
std::integral_constant<double, 1e-8>, // relativeTolerance
267-
std::integral_constant<int, 26>, // expected_iterations
267+
std::integral_constant<int, 27>, // expected_iterations
268268
std::integral_constant<double, 2e-6>, // expected_l2_error
269269
std::integral_constant<double, 9e-6>, // expected_inf_error
270270
std::integral_constant<double, 0.7> // expected_residual_reduction
@@ -299,7 +299,7 @@ using gmgpolar_test_suite = testing::Types<
299299
std::integral_constant<ResidualNormType, ResidualNormType::INFINITY_NORM>, // residualNormType
300300
std::integral_constant<double, 1e-12>, // absoluteTolerance
301301
std::integral_constant<double, 1e-10>, // relativeTolerance
302-
std::integral_constant<int, 12>, // expected_iterations
302+
std::integral_constant<int, 13>, // expected_iterations
303303
std::integral_constant<double, 6e-6>, // expected_l2_error
304304
std::integral_constant<double, 2e-5>, // expected_inf_error
305305
std::integral_constant<double, 0.3> // expected_residual_reduction
@@ -334,7 +334,7 @@ using gmgpolar_test_suite = testing::Types<
334334
std::integral_constant<ResidualNormType, ResidualNormType::EUCLIDEAN>, // residualNormType
335335
std::integral_constant<double, 1e-8>, // absoluteTolerance
336336
std::integral_constant<double, 1e-6>, // relativeTolerance
337-
std::integral_constant<int, 32>, // expected_iterations
337+
std::integral_constant<int, 34>, // expected_iterations
338338
std::integral_constant<double, 5e-4>, // expected_l2_error
339339
std::integral_constant<double, 2e-3>, // expected_inf_error
340340
std::integral_constant<double, 0.7> // expected_residual_reduction
@@ -472,7 +472,7 @@ using gmgpolar_test_suite = testing::Types<
472472
std::integral_constant<ResidualNormType, ResidualNormType::WEIGHTED_EUCLIDEAN>, // residualNormType
473473
std::integral_constant<double, 1e-8>, // absoluteTolerance
474474
std::integral_constant<double, -1.0>, // relativeTolerance
475-
std::integral_constant<int, 14>, // expected_iterations
475+
std::integral_constant<int, 16>, // expected_iterations
476476
std::integral_constant<double, 9e-5>, // expected_l2_error
477477
std::integral_constant<double, 3e-4>, // expected_inf_error
478478
std::integral_constant<double, 0.6> // expected_residual_reduction
@@ -506,7 +506,7 @@ using gmgpolar_test_suite = testing::Types<
506506
std::integral_constant<ResidualNormType, ResidualNormType::EUCLIDEAN>, // residualNormType
507507
std::integral_constant<double, 1e-9>, // absoluteTolerance
508508
std::integral_constant<double, 1e-8>, // relativeTolerance
509-
std::integral_constant<int, 7>, // expected_iterations
509+
std::integral_constant<int, 8>, // expected_iterations
510510
std::integral_constant<double, 5e-6>, // expected_l2_error
511511
std::integral_constant<double, 2e-5>, // expected_inf_error
512512
std::integral_constant<double, 0.2> // expected_residual_reduction

0 commit comments

Comments
 (0)