11 #include "serac/serac_config.hpp"
19 lin_solver_ = std::move(lin_solver);
25 std::unique_ptr<mfem::Solver> linear_solver,
26 std::unique_ptr<mfem::Solver> preconditioner)
28 SLIC_ERROR_ROOT_IF(!nonlinear_solver,
"Nonlinear solvers must be given to construct an EquationSolver");
29 SLIC_ERROR_ROOT_IF(!linear_solver,
"Linear solvers must be given to construct an EquationSolver");
31 nonlin_solver_ = std::move(nonlinear_solver);
32 lin_solver_ = std::move(linear_solver);
38 nonlin_solver_->SetOperator(op);
41 if (!nonlin_solver_set_solver_called_) {
43 nonlin_solver_set_solver_called_ =
true;
53 nonlin_solver_->Mult(
zero, x);
58 SLIC_ERROR_ROOT_IF(!superlu_mat_,
"Operator must be set prior to solving with SuperLU");
61 superlu_solver_.Mult(input, output);
74 int row_blocks = block_operator.NumRowBlocks();
75 int col_blocks = block_operator.NumColBlocks();
77 SLIC_ERROR_ROOT_IF(row_blocks != col_blocks,
"Attempted to use a direct solver on a non-square block system.");
79 mfem::Array2D<const mfem::HypreParMatrix*> hypre_blocks(row_blocks, col_blocks);
81 for (
int i = 0; i < row_blocks; ++i) {
82 for (
int j = 0; j < col_blocks; ++j) {
84 if (!block_operator.IsZeroBlock(i, j)) {
85 auto* hypre_block =
dynamic_cast<const mfem::HypreParMatrix*
>(&block_operator.GetBlock(i, j));
86 SLIC_ERROR_ROOT_IF(!hypre_block,
87 "Trying to use SuperLU on a block operator that does not contain HypreParMatrix blocks.");
89 hypre_blocks(i, j) = hypre_block;
91 hypre_blocks(i, j) =
nullptr;
97 return std::unique_ptr<mfem::HypreParMatrix>(mfem::HypreParMatrixFromBlocks(hypre_blocks));
103 auto* block_operator =
dynamic_cast<const mfem::BlockOperator*
>(&op);
106 if (block_operator) {
109 superlu_mat_ = std::make_unique<mfem::SuperLURowLocMatrix>(*monolithic_mat);
112 auto* matrix =
dynamic_cast<const mfem::HypreParMatrix*
>(&op);
114 SLIC_ERROR_ROOT_IF(!matrix,
"Matrix must be an assembled HypreParMatrix for use with SuperLU");
116 superlu_mat_ = std::make_unique<mfem::SuperLURowLocMatrix>(*matrix);
119 superlu_solver_.SetOperator(*superlu_mat_);
122 #ifdef MFEM_USE_STRUMPACK
124 void StrumpackSolver::Mult(
const mfem::Vector& input, mfem::Vector& output)
const
126 SLIC_ERROR_ROOT_IF(!strumpack_mat_,
"Operator must be set prior to solving with Strumpack");
129 strumpack_solver_.Mult(input, output);
132 void StrumpackSolver::SetOperator(
const mfem::Operator& op)
135 auto* block_operator =
dynamic_cast<const mfem::BlockOperator*
>(&op);
138 if (block_operator) {
141 strumpack_mat_ = std::make_unique<mfem::STRUMPACKRowLocMatrix>(*monolithic_mat);
144 auto* matrix =
dynamic_cast<const mfem::HypreParMatrix*
>(&op);
146 SLIC_ERROR_ROOT_IF(!matrix,
"Matrix must be an assembled HypreParMatrix for use with Strumpack");
148 strumpack_mat_ = std::make_unique<mfem::STRUMPACKRowLocMatrix>(*matrix);
151 strumpack_solver_.SetOperator(*strumpack_mat_);
158 std::unique_ptr<mfem::NewtonSolver> nonlinear_solver;
161 nonlinear_solver = std::make_unique<mfem::NewtonSolver>(comm);
163 nonlinear_solver = std::make_unique<mfem::LBFGSSolver>(comm);
167 #ifdef SERAC_USE_SUNDIALS
169 int kinsol_strat = KIN_NONE;
173 kinsol_strat = KIN_NONE;
176 kinsol_strat = KIN_LINESEARCH;
179 kinsol_strat = KIN_PICARD;
182 kinsol_strat = KIN_NONE;
183 SLIC_ERROR_ROOT(
"Unknown KINSOL nonlinear solver type given.");
185 auto kinsol_solver = std::make_unique<mfem::KINSolver>(comm, kinsol_strat,
true);
186 nonlinear_solver = std::move(kinsol_solver);
188 SLIC_ERROR_ROOT(
"KINSOL was not enabled when MFEM was built");
192 nonlinear_solver->SetRelTol(nonlinear_opts.
relative_tol);
193 nonlinear_solver->SetAbsTol(nonlinear_opts.
absolute_tol);
195 nonlinear_solver->SetPrintLevel(nonlinear_opts.
print_level);
200 nonlinear_solver->iterative_mode =
true;
202 return nonlinear_solver;
209 auto lin_solver = std::make_unique<SuperLUSolver>(linear_opts.
print_level, comm);
210 return {std::move(lin_solver),
nullptr};
213 #ifdef MFEM_USE_STRUMPACK
216 auto lin_solver = std::make_unique<StrumpackSolver>(linear_opts.
print_level, comm);
217 return {std::move(lin_solver),
nullptr};
222 std::unique_ptr<mfem::IterativeSolver> iter_lin_solver;
226 iter_lin_solver = std::make_unique<mfem::CGSolver>(comm);
229 iter_lin_solver = std::make_unique<mfem::GMRESSolver>(comm);
232 SLIC_ERROR_ROOT(
"Linear solver type not recognized.");
239 iter_lin_solver->SetPrintLevel(linear_opts.
print_level);
243 if (preconditioner) {
244 iter_lin_solver->SetPreconditioner(*preconditioner);
247 return {std::move(iter_lin_solver), std::move(preconditioner)};
251 std::unique_ptr<mfem::AmgXSolver> buildAMGX(
const AMGXOptions& options,
const MPI_Comm comm)
253 auto amgx = std::make_unique<mfem::AmgXSolver>();
254 conduit::Node options_node;
255 options_node[
"config_version"] = 2;
256 auto& solver_options = options_node[
"solver"];
257 solver_options[
"solver"] =
"AMG";
258 solver_options[
"presweeps"] = 1;
259 solver_options[
"postsweeps"] = 2;
260 solver_options[
"interpolator"] =
"D2";
261 solver_options[
"max_iters"] = 2;
262 solver_options[
"convergence"] =
"ABSOLUTE";
263 solver_options[
"cycle"] =
"V";
265 if (options.verbose) {
266 options_node[
"solver/obtain_timings"] = 1;
267 options_node[
"solver/monitor_residual"] = 1;
268 options_node[
"solver/print_solve_stats"] = 1;
275 static const auto solver_names = []() {
276 std::unordered_map<AMGXSolver, std::string> names;
294 options_node[
"solver/solver"] = solver_names.at(options.solver);
295 options_node[
"solver/smoother"] = solver_names.at(options.smoother);
298 amgx->ReadParameters(options_node.to_json(), mfem::AmgXSolver::INTERNAL);
299 amgx->InitExclusiveGPU(comm);
306 [[maybe_unused]] MPI_Comm comm)
308 std::unique_ptr<mfem::Solver> preconditioner_solver;
312 auto amg_preconditioner = std::make_unique<mfem::HypreBoomerAMG>();
313 amg_preconditioner->SetPrintLevel(print_level);
314 preconditioner_solver = std::move(amg_preconditioner);
316 auto jac_preconditioner = std::make_unique<mfem::HypreSmoother>();
317 jac_preconditioner->SetType(mfem::HypreSmoother::Type::Jacobi);
318 preconditioner_solver = std::move(jac_preconditioner);
320 auto jacl1_preconditioner = std::make_unique<mfem::HypreSmoother>();
321 jacl1_preconditioner->SetType(mfem::HypreSmoother::Type::l1Jacobi);
322 preconditioner_solver = std::move(jacl1_preconditioner);
324 auto gs_preconditioner = std::make_unique<mfem::HypreSmoother>();
325 gs_preconditioner->SetType(mfem::HypreSmoother::Type::GS);
326 preconditioner_solver = std::move(gs_preconditioner);
328 auto ilu_preconditioner = std::make_unique<mfem::HypreILU>();
329 ilu_preconditioner->SetLevelOfFill(1);
330 ilu_preconditioner->SetPrintLevel(print_level);
331 preconditioner_solver = std::move(ilu_preconditioner);
334 preconditioner_solver = buildAMGX(
AMGXOptions{}, comm);
336 SLIC_ERROR_ROOT(
"AMGX requested in non-GPU build");
339 SLIC_ERROR_ROOT_IF(preconditioner !=
Preconditioner::None,
"Unknown preconditioner type requested");
342 return preconditioner_solver;
347 auto& linear_container = container.addStruct(
"linear",
"Linear Equation Solver Parameters");
348 linear_container.required().registerVerifier([](
const axom::inlet::Container& container_to_verify) {
350 const bool is_iterative = (container_to_verify[
"type"].get<std::string>() ==
"iterative") &&
351 container_to_verify.contains(
"iterative_options");
352 const bool is_direct =
353 (container_to_verify[
"type"].get<std::string>() ==
"direct") && container_to_verify.contains(
"direct_options");
354 return is_iterative || is_direct;
358 linear_container.addString(
"type",
"The type of solver parameters to use (iterative|direct)")
360 .validValues({
"iterative",
"direct"});
362 auto& iterative_container = linear_container.addStruct(
"iterative_options",
"Iterative solver parameters");
363 iterative_container.addDouble(
"rel_tol",
"Relative tolerance for the linear solve.").defaultValue(1.0e-6);
364 iterative_container.addDouble(
"abs_tol",
"Absolute tolerance for the linear solve.").defaultValue(1.0e-8);
365 iterative_container.addInt(
"max_iter",
"Maximum iterations for the linear solve.").defaultValue(5000);
366 iterative_container.addInt(
"print_level",
"Linear print level.").defaultValue(0);
367 iterative_container.addString(
"solver_type",
"Solver type (gmres|minres|cg).").defaultValue(
"gmres");
368 iterative_container.addString(
"prec_type",
"Preconditioner type (JacobiSmoother|L1JacobiSmoother|AMG|ILU).")
369 .defaultValue(
"JacobiSmoother");
371 auto& direct_container = linear_container.addStruct(
"direct_options",
"Direct solver parameters");
372 direct_container.addInt(
"print_level",
"Linear print level.").defaultValue(0);
375 auto& nonlinear_container = container.addStruct(
"nonlinear",
"Newton Equation Solver Parameters").required(
false);
376 nonlinear_container.addDouble(
"rel_tol",
"Relative tolerance for the Newton solve.").defaultValue(1.0e-2);
377 nonlinear_container.addDouble(
"abs_tol",
"Absolute tolerance for the Newton solve.").defaultValue(1.0e-4);
378 nonlinear_container.addInt(
"max_iter",
"Maximum iterations for the Newton solve.").defaultValue(500);
379 nonlinear_container.addInt(
"print_level",
"Nonlinear print level.").defaultValue(0);
380 nonlinear_container.addString(
"solver_type",
"Solver type (Newton|KINFullStep|KINLineSearch)").defaultValue(
"Newton");
392 std::string
type = base[
"type"];
394 if (
type ==
"direct") {
396 options.
print_level = base[
"direct_options/print_level"];
400 auto config = base[
"iterative_options"];
405 std::string solver_type = config[
"solver_type"];
406 if (solver_type ==
"gmres") {
408 }
else if (solver_type ==
"cg") {
411 std::string msg = axom::fmt::format(
"Unknown Linear solver type given: '{0}'", solver_type);
412 SLIC_ERROR_ROOT(msg);
414 const std::string prec_type = config[
"prec_type"];
415 if (prec_type ==
"JacobiSmoother") {
417 }
else if (prec_type ==
"L1JacobiSmoother") {
419 }
else if (prec_type ==
"HypreAMG") {
421 }
else if (prec_type ==
"ILU") {
424 }
else if (prec_type ==
"AMGX") {
427 }
else if (prec_type ==
"GaussSeidel") {
430 std::string msg = axom::fmt::format(
"Unknown preconditioner type given: '{0}'", prec_type);
431 SLIC_ERROR_ROOT(msg);
444 const std::string solver_type = base[
"solver_type"];
445 if (solver_type ==
"Newton") {
447 }
else if (solver_type ==
"KINFullStep") {
449 }
else if (solver_type ==
"KINLineSearch") {
451 }
else if (solver_type ==
"KINPicard") {
454 SLIC_ERROR_ROOT(axom::fmt::format(
"Unknown nonlinear solver type given: '{0}'", solver_type));
467 std::move(preconditioner));
This class manages the objects typically required to solve a nonlinear set of equations arising from ...
void setOperator(const mfem::Operator &op)
void solve(mfem::Vector &x) const
mfem::Solver * preconditioner()
static void defineInputFileSchema(axom::inlet::Container &container)
EquationSolver(std::unique_ptr< mfem::NewtonSolver > nonlinear_solver, std::unique_ptr< mfem::Solver > linear_solver, std::unique_ptr< mfem::Solver > preconditioner=nullptr)
mfem::Solver & linearSolver()
void Mult(const mfem::Vector &input, mfem::Vector &output) const
Factor and solve the linear system y = Op^{-1} x using DSuperLU.
void SetOperator(const mfem::Operator &op)
Set the underlying matrix operator to use in the solution algorithm.
This file contains the declaration of an equation solver wrapper.
This file contains the all the necessary functions and macros required for logging as well as a helpe...
Accelerator functionality.
@ KINBacktrackingLineSearch
void exitGracefully(bool error)
Exits the program gracefully after cleaning up necessary tasks.
std::pair< std::unique_ptr< mfem::Solver >, std::unique_ptr< mfem::Solver > > buildLinearSolverAndPreconditioner(LinearSolverOptions linear_opts, MPI_Comm comm)
Build the linear solver and its associated preconditioner given a linear options struct.
Preconditioner
The type of preconditioner to be used.
std::unique_ptr< mfem::Solver > buildPreconditioner(Preconditioner preconditioner, int print_level, [[maybe_unused]] MPI_Comm comm)
Build a preconditioner from the available options.
constexpr SERAC_HOST_DEVICE auto type(const tuple< T... > &values)
a function intended to be used for extracting the ith type from a tuple.
std::unique_ptr< mfem::NewtonSolver > buildNonlinearSolver(NonlinearSolverOptions nonlinear_opts, MPI_Comm comm)
Build a nonlinear solver using the nonlinear option struct.
std::unique_ptr< mfem::HypreParMatrix > buildMonolithicMatrix(const mfem::BlockOperator &block_operator)
Function for building a monolithic parallel Hypre matrix from a block system of smaller Hypre matrice...
serac::EquationSolver operator()(const axom::inlet::Container &base)
Returns created object from Inlet container.
serac::LinearSolverOptions operator()(const axom::inlet::Container &base)
Returns created object from Inlet container.
serac::NonlinearSolverOptions operator()(const axom::inlet::Container &base)
Returns created object from Inlet container.
Stores the information required to configure a NVIDIA AMGX preconditioner.
Parameters for an iterative linear solution scheme.
Preconditioner preconditioner
PreconditionerOptions selection.
LinearSolver linear_solver
Linear solver selection.
double relative_tol
Relative tolerance.
int preconditioner_print_level
Debugging print level for the preconditioner.
int max_iterations
Maximum number of iterations.
int print_level
Debugging print level for the linear solver.
double absolute_tol
Absolute tolerance.
Nonlinear solution scheme parameters.
int print_level
Debug print level.
double relative_tol
Relative tolerance.
NonlinearSolver nonlin_solver
Nonlinear solver selection.
double absolute_tol
Absolute tolerance.
int max_iterations
Maximum number of iterations.
A sentinel struct for eliding no-op tensor operations.
Helper functions for exiting Serac cleanly.