Skip to content

Refactor preconditioner implementation#167

Open
greole wants to merge 30 commits into
devfrom
refact/precond
Open

Refactor preconditioner implementation#167
greole wants to merge 30 commits into
devfrom
refact/precond

Conversation

@greole
Copy link
Copy Markdown
Collaborator

@greole greole commented Sep 15, 2025

This PR refactors the preconditioner implementation by placing the generation into separate files. Additionally, a central coarse solver generation routine is added.

@greole greole changed the base branch from dev to stack/ogl_0.6 September 15, 2025 09:43
@greole greole changed the base branch from stack/ogl_0.6 to enh/repart_comm September 15, 2025 09:43
@greole greole changed the title Refact/precond Refactor preconditioner implementation Sep 17, 2025
@greole greole requested a review from Copilot September 17, 2025 09:00
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR refactors the preconditioner implementation by extracting the generation logic from a monolithic class into separate, focused header files. The main changes involve moving preconditioner-specific code into dedicated classes while introducing a centralized coarse solver generation routine for better code organization and maintainability.

  • Extracted preconditioner implementations into dedicated header files (Jacobi, LU, Cholesky, ISAI, Multigrid)
  • Added a centralized coarse solver generation function in CoarseSolver.cpp
  • Introduced utility functions for Schwarz wrapping operations

Reviewed Changes

Copilot reviewed 12 out of 14 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
src/Preconditioner/CoarseSolver.cpp New implementation for centralized coarse solver generation
include/OGL/StoppingCriterion.hpp Minor formatting improvement to debug message
include/OGL/Preconditioner/Schwarz.hpp New utility functions for wrapping preconditioners with Schwarz methods
include/OGL/Preconditioner/PreconditionerWrapper.hpp New base wrapper class definition
include/OGL/Preconditioner/Multigrid.hpp New dedicated Multigrid preconditioner class
include/OGL/Preconditioner/LU.hpp New LU factorization preconditioner class
include/OGL/Preconditioner/Jacobi.hpp New Block Jacobi preconditioner class
include/OGL/Preconditioner/ISAI.hpp New ISAI preconditioner class
include/OGL/Preconditioner/CoarseSolver.hpp Header declaration for coarse solver generation function
include/OGL/Preconditioner/Cholesky.hpp New Cholesky factorization preconditioner class
include/OGL/Preconditioner.hpp Refactored main class to use new dedicated preconditioner classes
CMakeLists.txt Added new source file to build configuration

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +43 to +69
if (solver == "CG") {
coarsest_solver = gko::share(cg::build()
.with_preconditioner(bjfac)
.with_criteria(solve_it, solve_red)
.on(exec));
}
if (solver == "BiCGStab") {
coarsest_solver = gko::share(bicgstab::build()
.with_preconditioner(bjfac)
.with_criteria(solve_it, solve_red)
.on(exec));
}
if (solver == "GMRES") {
coarsest_solver = gko::share(gmres::build()
.with_preconditioner(bjfac)
.with_criteria(solve_it, solve_red)
.on(exec));
}
if (solver == "Jacobi") {
scalar relaxFac{
solverDict.lookupOrDefault("relaxationFactor", scalar(0.9))};
coarsest_solver = gko::share(ir::build()
.with_solver(bjfac)
.with_relaxation_factor(relaxFac)
.with_criteria(solve_it, solve_red)
.on(exec));
}
Copy link

Copilot AI Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This chain of if statements should use else-if to avoid unnecessary condition checks once a match is found. The current implementation continues checking all conditions even after finding a match.

Copilot uses AI. Check for mistakes.
Comment thread include/OGL/Preconditioner/Schwarz.hpp Outdated
Comment thread include/OGL/Preconditioner/Schwarz.hpp Outdated
auto coarsening = d.lookupOrDefault<word>("coarsening", word("PGM"));
auto coarseWeight = d.lookupOrDefault("coarseWeight", scalar(0.1));

if (coarsening == "fixed") {
Copy link

Copilot AI Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function has multiple return paths and incomplete logic. The function can return from either the 'fixed' or 'PGM' coarsening branches, but there's no return statement at the end for unhandled cases, which could lead to undefined behavior.

Copilot uses AI. Check for mistakes.
Comment thread include/OGL/Preconditioner/Multigrid.hpp Outdated
Comment on lines +43 to +45
std::shared_ptr<gko::LinOp> generate_factorization() const
{
if (factorization_ == "ILU") {
Copy link

Copilot AI Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function generate_factorization() has multiple return paths through if statements but no return statement at the end, which could lead to undefined behavior if none of the conditions are met.

Copilot uses AI. Check for mistakes.
Comment on lines +63 to +73
virtual std::shared_ptr<gko::LinOp> create()
{
if (type_ == "SPD") {
return dispatch_schwarz(mtx_, exec_, generate_precond_factory_spd(),
d_, verbose_);
}
if (type_ == "General") {
return dispatch_schwarz(
mtx_, exec_, generate_precond_factory_general(), d_, verbose_);
}
}
Copy link

Copilot AI Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function has multiple return paths but no return statement at the end, which could lead to undefined behavior if none of the type conditions are met.

Copilot uses AI. Check for mistakes.
Comment on lines +43 to +45
std::shared_ptr<gko::LinOp> generate_factorization() const
{
if (factorization_ == "IC") {
Copy link

Copilot AI Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function generate_factorization() has multiple return paths through if statements but no return statement at the end, which could lead to undefined behavior if none of the conditions are met.

Copilot uses AI. Check for mistakes.
@greole greole changed the base branch from enh/repart_comm to dev May 7, 2026 16:02
@greole greole force-pushed the refact/precond branch from 8e737df to 723e510 Compare May 9, 2026 10:06
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 21 out of 21 changed files in this pull request and generated 21 comments.

Comment on lines +204 to +218
word frequencyMode = "optimizer";
label minIter = minIter_;
label frequency = frequency_;
// in case of export_res all residuals need to be computed
if (!export_res) {
if (prev_solve_iters > 0 && adapt_minIter_ && prev_rel_cost > 0) {
minIter = prev_solve_iters * relaxationFactor_;
auto alpha =
sqrt(1.0 / (prev_solve_iters * (1.0 - relaxationFactor_)) *
prev_rel_cost);
frequency = min(norm_eval_limit_, max(1, label(1 / alpha)));
if (frequencyMode == "optimizer") {
auto alpha = sqrt(
1.0 / (prev_solve_iters * (1.0 - relaxationFactor_)) *
prev_rel_cost);
frequency = min(norm_eval_limit_, max(1, label(1 / alpha)));
}
if (frequencyMode == "relative") {
frequency = label(prev_solve_iters * 0.075) + 1;
std::to_string(frequency);
std::to_string(frequency) + " prev_solve_iters " +
std::to_string(prev_solve_iters) + " adapt_minIter_ " +
std::to_string(adapt_minIter_) + " prev_rel_cost ";
Comment thread src/StoppingCriterion.cpp
Comment on lines +48 to +55
auto start_axref = std::chrono::steady_clock::now();
compute_Axref_dist(global_size[0], local_size[0], device_exec, gkomatrix, x,
Axref);

auto end_axref = std::chrono::steady_clock::now();
auto delta_t_axref = std::chrono::duration_cast<std::chrono::microseconds>(
end_axref - start_axref)
.count() /
1.0;
const AllToAllPattern allToAllIn,
label start_rank)
{
auto host_comm = exec_handler.get_host_comm();
Comment on lines +202 to +203
send_offsets.back() = scalar(allToAllIn.send_offsets.back());
recv_offsets.back() = scalar(allToAllIn.recv_offsets.back());
Comment on lines +59 to +66
if (factorization_ == "ParILU") {
label iterations = d_.lookupOrDefault("iterations", label(5));
auto factorization_factory =
gko::factorization::ParIlut<scalar, label>::build()
.with_skip_sorting(skip_sorting_)
.with_iterations(iterations)
.on(exec_);

Comment on lines +34 to +74
type_(d.lookupOrDefault("type", word("general"))),
sparsityPower_(d.lookupOrDefault("sparsityPower", label(1)))
{
word msg = "Generate " + type_ + "ISAI" +
" preconditioner:\n\tsparsityPower: " +
std::to_string(sparsityPower_);
MLOG_0(verbose_, msg)
}

auto generate_precond_factory_spd()
{
return gko::preconditioner::Isai<gko::preconditioner::isai_type::spd,
scalar, label>::build()
.with_skip_sorting(skip_sorting_)
.with_sparsity_power(sparsityPower_)
.on(exec_);
}

auto generate_precond_factory_general()
{
return gko::preconditioner::Isai<
gko::preconditioner::isai_type::general, scalar,
label>::build()
.with_skip_sorting(skip_sorting_)
.with_sparsity_power(sparsityPower_)
.on(exec_);
}

virtual std::shared_ptr<gko::LinOp> create()
{
if (type_ == "SPD") {
auto b = generate_precond_factory_spd();
return dispatch_schwarz(
mtx_, exec_,
gko::share(
b->generate(gko::as<RepartDistMatrix>(mtx_)->get_local())),
d_, verbose_);
}
if (type_ == "General") {
auto b = generate_precond_factory_general();
return dispatch_schwarz(
Comment on lines +22 to +24
/* @brief computes AllToAllPattern for repartioned communincator from global
* allToAll pattern by discarding all zero communication before and after the
* repartioner scope.
Comment on lines +124 to +125
// NOTE in case of symmetric matrix 0 (upper) is same as 1 (lower)
// thus we can start at 1
Comment on lines +70 to +72
auto solveNormC =
d.lookupOrDefault("reductionCoarseSolver", label(1e-6));
auto maxIterCoarse = d.lookupOrDefault("maxIterCoarse", label(50));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants