PRISMS-PF Manual
Loading...
Searching...
No Matches
linear_solver.h
Go to the documentation of this file.
1// SPDX-FileCopyrightText: © 2025 PRISMS Center at the University of Michigan
2// SPDX-License-Identifier: GNU Lesser General Public Version 2.1
3
4#pragma once
5
6#include <deal.II/lac/diagonal_matrix.h>
7#include <deal.II/lac/precondition.h>
8#include <deal.II/lac/solver_control.h>
9#include <deal.II/lac/solver_selector.h>
10
14#include <prismspf/core/timer.h>
16#include <prismspf/core/types.h>
17
20
22
23#include <prismspf/config.h>
24
26
27template <unsigned int dim, unsigned int degree, typename number>
28class SolveContext;
29
34template <unsigned int dim, unsigned int degree, typename number>
35class LinearSolver : public SolverBase<dim, degree, number>
36{
37protected:
38 using SolverBase<dim, degree, number>::solutions;
39 using SolverBase<dim, degree, number>::solve_context;
40 using SolverBase<dim, degree, number>::solve_block;
42 dealii::PreconditionChebyshev<MFOperator<dim, degree, number>,
44 dealii::DiagonalMatrix<BlockVector<number>>>;
45
46public:
52 const SolveContext<dim, degree, number> &_solve_context)
53 : SolverBase<dim, degree, number>(_solve_block, _solve_context)
54 , rhs_operator(solve_context->get_pde_operator(),
55 &PDEOperatorBase<dim, degree, number>::compute_rhs,
56 solve_context->get_field_attributes(),
57 solve_context->get_solution_indexer(),
58 solve_context->get_matrix_free_manager(),
59 solve_block.dependencies_rhs,
60 solve_context->get_simulation_timer())
61 , lhs_operator(solve_context->get_pde_operator(),
62 &PDEOperatorBase<dim, degree, number>::compute_lhs,
63 solve_context->get_field_attributes(),
64 solve_context->get_solution_indexer(),
65 solve_context->get_matrix_free_manager(),
66 solve_block.dependencies_lhs,
67 solve_context->get_simulation_timer())
68 {}
69
73 void
74 init(const std::list<DependencyMap> &all_dependeny_sets) override
75 {
76 SolverBase<dim, degree, number>::init(all_dependeny_sets);
77 rhs_vector.reinit(solutions.get_solution_full_vector(0));
78
79 // Initialize rhs_operator
80 rhs_operator.initialize(solutions);
81 rhs_operator.set_scaling_diagonal(lin_params().tolerance_type != AbsoluteResidual,
82 solve_context->get_invm_manager().get_invm_sqrt(
83 solve_context->get_field_attributes(),
84 solve_block.field_indices,
85 0));
86 // Initialize lhs_operator
87 lhs_operator.initialize(solutions);
88 lhs_operator.set_scaling_diagonal(lin_params().tolerance_type != AbsoluteResidual,
89 solve_context->get_invm_manager().get_invm_sqrt(
90 solve_context->get_field_attributes(),
91 solve_block.field_indices,
92 0));
93
94 linear_solver_control.set_max_steps(lin_params().max_iterations);
95 linear_solver_control.set_tolerance(lin_params().tolerance * normalization_value());
97 inhomogeneous_values.reinit(solutions.get_solution_full_vector(0));
98 solutions.apply_constraints(inhomogeneous_values, 0);
99 inhomogeneous_rhs.reinit(solutions.get_solution_full_vector(0));
101 }
102
106 void
107 reinit() override
108 {
110 rhs_vector.reinit(solutions.get_solution_full_vector(0));
111
112 inhomogeneous_values.reinit(solutions.get_solution_full_vector(0));
113 solutions.apply_constraints(inhomogeneous_values, 0);
114 inhomogeneous_rhs.reinit(solutions.get_solution_full_vector(0));
116 }
117
121 void
122 solve_impl() override
123 {
124 // Zero out the ghosts
125 Timer::start_section("Zero ghosts");
126 solutions.zero_out_ghosts(0);
127 Timer::end_section("Zero ghosts");
128
129 // Set up rhs vector
130 rhs_operator.compute_operator(rhs_vector);
131
132 // Note 1. Use the previous result of the linear solve without nonzero dirichlet
133 // as the initial guess in the next increment. See Note 2. `inhomogeneous_rhs` is
134 // not actually what it is being used as here, we just don't want to allocate a
135 // whole new vector for this purpose
136 solutions.get_solution_full_vector(0).swap(inhomogeneous_rhs);
137 // Get the homogeneous rhs
138 lhs_operator.read_plain = true;
140 lhs_operator.read_plain = false;
142
143 // Linear solve
144 do_linear_solve(rhs_vector, lhs_operator, solutions.get_solution_full_vector(0));
145
146 // Note 2. Make a copy of the solution to use as the initial guess in the next
147 // increment. See Note 1. `inhomogeneous_rhs` is not actually what it is being
148 // used as here, we just don't want to allocate a whole new vector for this
149 // purpose
150 inhomogeneous_rhs = solutions.get_solution_full_vector(0);
151 // Add back in nonzero dirichlet conditions
152 solutions.get_solution_full_vector(0) += inhomogeneous_values;
153
154 // Apply constraints
155 solutions.apply_constraints(0);
156
157 // Update the ghosts
158 Timer::start_section("Update ghosts");
159 solutions.update_ghosts(0);
160 Timer::end_section("Update ghosts");
161 }
162
163 int
166 BlockVector<number> &x_vector)
167 {
168 // Linear solve
169 try
170 {
171 if (lin_params().preconditioner == None)
172 {
173 lin_solver.solve(lhs_matrix,
174 x_vector,
175 b_vector,
176 dealii::PreconditionIdentity());
177 }
178 if (lin_params().preconditioner == Chebyshev)
179 {
180 lhs_matrix.reinit_matrix_diagonal(x_vector);
181 lhs_matrix.eval_matrix_diagonal();
182
183 lin_solver.solve(lhs_matrix, x_vector, b_vector, precond_chebyshev);
184 }
185 }
186 catch (dealii::SolverControl::NoConvergence &exc)
187 {
189 << "[Increment " << solve_context->get_simulation_timer().get_increment()
190 << "] "
191 << "Warning: linear solver did not converge as per set tolerances before "
192 << lin_params().max_iterations << " iterations.\n";
193 }
194 if (solve_context->get_user_inputs().output_parameters.should_output(
195 solve_context->get_simulation_timer().get_increment()))
196 {
198 << " Linear solve final residual : "
200 << " Linear steps: " << linear_solver_control.last_step() << "\n"
201 << std::flush;
202 }
203 return linear_solver_control.last_step();
204 }
205
206protected:
211
214
215 double
217 {
219 double value = 1.0;
220 if (type == RMSEPerField || type == RMSETotal)
221 {
222 value *= std::sqrt(solve_context->get_triangulation_manager().get_volume());
223 }
224 if (type == RMSEPerField || type == IntegratedPerField)
225 {
226 value *= std::sqrt(double(solve_block.field_indices.size()));
227 }
228 return value;
229 }
230
231 void
233 {
234 if (lin_params().preconditioner == None)
235 {
236 void(0); // do nothing
237 }
238 if (lin_params().preconditioner == Chebyshev)
239 {
241 }
242 }
243
244 void
246 {
247 const auto &chebyshev_params = lin_params().chebyshev_parameters;
248 precond_data.degree = chebyshev_params.degree;
249 precond_data.smoothing_range = chebyshev_params.smoothing_range; // ≈ λ_min / λ_max
250 precond_data.eig_cg_n_iterations = chebyshev_params.eig_cg_n_iterations;
251
252 lhs_operator.reinit_matrix_diagonal(solutions.get_solution_full_vector(0));
253 precond_data.preconditioner = lhs_operator.get_matrix_diagonal_inverse();
254
256 }
257
258 void
260 {
261 const auto &richardson_parameters = lin_params().richardson_parameters;
262 const auto &bicgstab_parameters = lin_params().bicgstab_parameters;
263 const auto &gmres_parameters = lin_params().gmres_parameters;
264 const typename dealii::SolverRichardson<BlockVector<number>>::AdditionalData
265 local_richardson_parameters(richardson_parameters.omega,
266 richardson_parameters.use_preconditioned_residual);
267 const typename dealii::SolverBicgstab<BlockVector<number>>::AdditionalData
268 local_bicgstab_parameters(bicgstab_parameters.exact_residual,
269 bicgstab_parameters.breakdown);
270 const typename dealii::SolverGMRES<BlockVector<number>>::AdditionalData
271 local_gmres_parameters(gmres_parameters.max_basis_size,
272 gmres_parameters.right_preconditioning,
273 gmres_parameters.use_default_residual,
274 gmres_parameters.force_re_orthogonalization,
275 gmres_parameters.batched_mode,
276 gmres_parameters.orthogonalization_strategy);
277 const typename dealii::SolverFGMRES<BlockVector<number>>::AdditionalData
278 local_fgmres_parameters(gmres_parameters.max_basis_size,
279 gmres_parameters.orthogonalization_strategy);
280
281 lin_solver.set_data(local_richardson_parameters);
282 lin_solver.set_data(local_bicgstab_parameters);
283 lin_solver.set_data(local_gmres_parameters);
284 lin_solver.set_data(local_fgmres_parameters);
285
286 lin_solver.select(lin_params().solver_type);
288 }
289
290private:
296 {
297 return solve_block.linear_solver_parameters;
298 }
299
303 dealii::SolverControl linear_solver_control;
304
308 dealii::SolverSelector<BlockVector<number>> lin_solver;
309
315
320
322 PreconditionChebyshev::AdditionalData precond_data;
323};
324
325PRISMS_PF_END_NAMESPACE
static dealii::ConditionalOStream & pout_summary()
Log output stream for writing a summary.log file.
Definition conditional_ostreams.cc:35
static dealii::ConditionalOStream & pout_base()
Generic parallel output stream. Used for essential information in release and debug mode.
Definition conditional_ostreams.cc:44
BlockVector< number > inhomogeneous_values
Vector containing only the inhomogeneous constraints (namely, non-zero Dirichlet values)
Definition linear_solver.h:314
void initialize_preconditioner()
Definition linear_solver.h:232
MFOperator< dim, degree, number > rhs_operator
Matrix free operators.
Definition linear_solver.h:210
void initialize_solver()
Definition linear_solver.h:259
BlockVector< number > rhs_vector
Definition linear_solver.h:213
const LinearSolverParameters & lin_params() const
Linear solver parameters.
Definition linear_solver.h:295
double normalization_value()
Definition linear_solver.h:216
PreconditionChebyshev precond_chebyshev
Definition linear_solver.h:321
int do_linear_solve(BlockVector< number > &b_vector, MFOperator< dim, degree, number > &lhs_matrix, BlockVector< number > &x_vector)
Definition linear_solver.h:164
PreconditionChebyshev::AdditionalData precond_data
Definition linear_solver.h:322
MFOperator< dim, degree, number > lhs_operator
Definition linear_solver.h:212
void init(const std::list< DependencyMap > &all_dependeny_sets) override
Initialize the solver.
Definition linear_solver.h:74
BlockVector< number > inhomogeneous_rhs
Result of the linear operator applied to the inhomogeneous values.
Definition linear_solver.h:319
dealii::SolverSelector< BlockVector< number > > lin_solver
Solver. Can switch between different linear solvers.
Definition linear_solver.h:308
dealii::PreconditionChebyshev< MFOperator< dim, degree, number >, BlockVector< number >, dealii::DiagonalMatrix< BlockVector< number > > > PreconditionChebyshev
Definition linear_solver.h:41
void initialize_chebyshev()
Definition linear_solver.h:245
dealii::SolverControl linear_solver_control
Solver control. Contains max iterations and tolerance.
Definition linear_solver.h:303
void solve_impl() override
Solve for a single update step.
Definition linear_solver.h:122
LinearSolver(SolveBlock _solve_block, const SolveContext< dim, degree, number > &_solve_context)
Constructor.
Definition linear_solver.h:51
void reinit() override
Reinitialize the solver.
Definition linear_solver.h:107
This class exists to evaluate a single user-defined operator for the matrix-free implementation of so...
Definition mf_operator.h:55
void eval_matrix_diagonal()
Evaluate matrix diagonal (and inverse).
Definition mf_operator.cc:221
void reinit_matrix_diagonal(const BlockVector< number > &shape)
Reinit diagonal matrix to have the correct shape.
Definition mf_operator.cc:213
This class contains the user implementation of each PDE operator.
Definition pde_operator_base.h:27
Structure to hold the attributes of a solve-block.
Definition solve_block.h:59
This class provides context for a solver with ptrs to all the relevant dependencies.
Definition solve_context.h:34
virtual void reinit()
Reinitialize the solution vectors & apply constraints.
Definition solver_base.h:97
SolverBase(SolveBlock _solve_block, const SolveContext< dim, degree, number > &_solve_context)
Constructor.
Definition solver_base.h:36
virtual void init(const std::list< DependencyMap > &all_dependeny_sets)
Initialize the solver.
Definition solver_base.h:84
GroupSolutionHandler< dim, number > solutions
Solution vectors for fields handled by this solver.
Definition solver_base.h:271
const SolveContext< dim, degree, number > * solve_context
Solver context provides access to external information.
Definition solver_base.h:266
SolveBlock solve_block
Information about the solve block this handler is responsible for.
Definition solver_base.h:261
static void start_section(const char *name)
Start a new timer section.
Definition timer.cc:116
static void end_section(const char *name)
End the timer section.
Definition timer.cc:127
dealii::LinearAlgebra::distributed::BlockVector< number > BlockVector
Typedef for solution block vector.
Definition group_solution_handler.h:29
Definition conditional_ostreams.cc:20
Struct that stores relevant linear solve information of a certain solve block.
Definition linear_solve_parameters.h:25
unsigned int max_iterations
Definition linear_solve_parameters.h:36
dealii::SolverGMRES ::AdditionalData gmres_parameters
Definition linear_solve_parameters.h:45
SolverToleranceType tolerance_type
Definition linear_solve_parameters.h:33
dealii::PreconditionChebyshev ::AdditionalData chebyshev_parameters
Definition linear_solve_parameters.h:41
dealii::SolverBicgstab ::AdditionalData bicgstab_parameters
Definition linear_solve_parameters.h:44
dealii::SolverRichardson ::AdditionalData richardson_parameters
Definition linear_solve_parameters.h:43
SolverToleranceType
Solver tolerance type.
Definition type_enums.h:112
@ RMSEPerField
The mean local error averaged over each field is lower than the tolerance.
Definition type_enums.h:124
@ AbsoluteResidual
Legacy.
Definition type_enums.h:116
@ RMSETotal
The sum of the average local errors of each field is lower than the tolerance.
Definition type_enums.h:132
@ IntegratedPerField
The integrated error averaged over each field is lower than the tolerance.
Definition type_enums.h:128
@ Chebyshev
Definition type_enums.h:145
@ None
Definition type_enums.h:144