Line data Source code
1 : // The libMesh Finite Element Library.
2 : // Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 :
4 : // This library is free software; you can redistribute it and/or
5 : // modify it under the terms of the GNU Lesser General Public
6 : // License as published by the Free Software Foundation; either
7 : // version 2.1 of the License, or (at your option) any later version.
8 :
9 : // This library is distributed in the hope that it will be useful,
10 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : // Lesser General Public License for more details.
13 :
14 : // You should have received a copy of the GNU Lesser General Public
15 : // License along with this library; if not, write to the Free Software
16 : // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 :
18 :
19 : #include "libmesh/libmesh_config.h"
20 :
21 : // Currently, the EigenSystem should only be available
22 : // if SLEPc support is enabled.
23 : #if defined(LIBMESH_HAVE_SLEPC)
24 :
25 : // Local includes
26 : #include "libmesh/eigen_system.h"
27 : #include "libmesh/equation_systems.h"
28 : #include "libmesh/sparse_matrix.h"
29 : #include "libmesh/shell_matrix.h"
30 : #include "libmesh/eigen_solver.h"
31 : #include "libmesh/dof_map.h"
32 : #include "libmesh/mesh_base.h"
33 : #include "libmesh/enum_eigen_solver_type.h"
34 :
35 : namespace libMesh
36 : {
37 :
38 :
39 348 : EigenSystem::EigenSystem (EquationSystems & es,
40 : const std::string & name_in,
41 : const unsigned int number_in
42 348 : ) :
43 : Parent (es, name_in, number_in),
44 332 : matrix_A (nullptr),
45 332 : matrix_B (nullptr),
46 332 : precond_matrix (nullptr),
47 332 : eigen_solver (EigenSolver<Number>::build(es.comm())),
48 332 : _n_converged_eigenpairs (0),
49 332 : _n_iterations (0),
50 332 : _eigen_problem_type (NHEP),
51 332 : _use_shell_matrices (false),
52 348 : _use_shell_precond_matrix(false)
53 : {
54 348 : }
55 :
56 :
57 :
58 464 : EigenSystem::~EigenSystem () = default;
59 :
60 :
61 :
62 0 : void EigenSystem::clear ()
63 : {
64 : // Clear the parent data
65 0 : Parent::clear();
66 :
67 : // The SparseMatrices are contained in _matrices
68 : // and are cleared with the above call
69 0 : matrix_A = nullptr;
70 0 : matrix_B = nullptr;
71 0 : precond_matrix = nullptr;
72 :
73 : // Operators
74 0 : shell_matrix_A.reset();
75 0 : shell_matrix_B.reset();
76 :
77 : // Preconditioning matrices
78 0 : shell_precond_matrix.reset();
79 :
80 : // clear the solver
81 0 : eigen_solver->clear();
82 0 : }
83 :
84 :
85 278 : void EigenSystem::set_eigenproblem_type (EigenProblemType ept)
86 : {
87 278 : if (!can_add_matrices())
88 0 : libmesh_error_msg("ERROR: Cannot change eigen problem type after system initialization");
89 :
90 278 : _eigen_problem_type = ept;
91 :
92 6 : eigen_solver->set_eigenproblem_type(ept);
93 :
94 : // libMesh::out << "The Problem type is set to be: " << std::endl;
95 :
96 278 : switch (_eigen_problem_type)
97 : {
98 0 : case HEP:
99 : // libMesh::out << "Hermitian" << std::endl;
100 0 : break;
101 :
102 0 : case NHEP:
103 : // libMesh::out << "Non-Hermitian" << std::endl;
104 0 : break;
105 :
106 6 : case GHEP:
107 : // libMesh::out << "Generalized Hermitian" << std::endl;
108 6 : break;
109 :
110 0 : case GNHEP:
111 : // libMesh::out << "Generalized Non-Hermitian" << std::endl;
112 0 : break;
113 :
114 0 : case GHIEP:
115 : // libMesh::out << "Generalized indefinite Hermitian" << std::endl;
116 0 : break;
117 :
118 0 : default:
119 : // libMesh::out << "not properly specified" << std::endl;
120 0 : libmesh_error_msg("Unrecognized _eigen_problem_type = " << _eigen_problem_type);
121 : }
122 278 : }
123 :
124 :
125 :
126 348 : void EigenSystem::add_matrices ()
127 : {
128 348 : Parent::add_matrices();
129 :
130 348 : if (_use_shell_matrices)
131 : {
132 66 : if (!shell_matrix_A)
133 132 : shell_matrix_A = ShellMatrix<Number>::build(this->comm());
134 :
135 66 : if (generalized() && !shell_matrix_B)
136 132 : shell_matrix_B = ShellMatrix<Number>::build(this->comm());
137 :
138 66 : if (_use_shell_precond_matrix)
139 : {
140 0 : if (!shell_precond_matrix)
141 0 : shell_precond_matrix = ShellMatrix<Number>::build(this->comm());
142 : }
143 66 : else if (!precond_matrix)
144 66 : precond_matrix = &(this->add_matrix("Eigen Preconditioner"));
145 : }
146 : else
147 : {
148 282 : if (!matrix_A)
149 282 : matrix_A = &(this->add_matrix("Eigen Matrix A"));
150 :
151 282 : if (generalized() && !matrix_B)
152 212 : matrix_B = &(this->add_matrix("Eigen Matrix B"));
153 : }
154 348 : }
155 :
156 :
157 :
158 348 : void EigenSystem::init_matrices ()
159 : {
160 348 : Parent::init_matrices();
161 :
162 348 : const bool condense_constraints = condense_constrained_dofs();
163 348 : if (shell_matrix_A)
164 : {
165 0 : shell_matrix_A->attach_dof_map(this->get_dof_map());
166 66 : if (condense_constraints)
167 0 : shell_matrix_A->omit_constrained_dofs();
168 66 : shell_matrix_A->init();
169 : }
170 :
171 348 : if (shell_matrix_B)
172 : {
173 0 : shell_matrix_B->attach_dof_map(this->get_dof_map());
174 66 : if (condense_constraints)
175 0 : shell_matrix_B->omit_constrained_dofs();
176 66 : shell_matrix_B->init();
177 : }
178 :
179 348 : if (shell_precond_matrix)
180 : {
181 0 : shell_precond_matrix->attach_dof_map(this->get_dof_map());
182 0 : if (condense_constraints)
183 0 : shell_precond_matrix->omit_constrained_dofs();
184 0 : shell_precond_matrix->init();
185 : }
186 348 : }
187 :
188 :
189 0 : void EigenSystem::reinit ()
190 : {
191 : // initialize parent data
192 : // this calls reinit on matrix_A, matrix_B, and precond_matrix (if any)
193 0 : Parent::reinit();
194 :
195 0 : if (shell_matrix_A)
196 : {
197 0 : shell_matrix_A->clear();
198 0 : shell_matrix_A->init();
199 : }
200 :
201 0 : if (shell_matrix_B)
202 : {
203 0 : shell_matrix_B->clear();
204 0 : shell_matrix_B->init();
205 : }
206 :
207 0 : if (shell_precond_matrix)
208 : {
209 0 : shell_precond_matrix->clear();
210 0 : shell_precond_matrix->init();
211 : }
212 0 : }
213 :
214 : void
215 359 : EigenSystem::solve_helper(SparseMatrix<Number> * const A,
216 : SparseMatrix<Number> * const B,
217 : SparseMatrix<Number> * const P)
218 : {
219 : // A reference to the EquationSystems
220 16 : EquationSystems & es = this->get_equation_systems();
221 :
222 : // Get the tolerance for the solver and the maximum
223 : // number of iterations. Here, we simply adopt the linear solver
224 : // specific parameters.
225 710 : const double tol = parameters.have_parameter<Real>("linear solver tolerance") ?
226 0 : double(parameters.get<Real>("linear solver tolerance")) :
227 710 : double(es.parameters.get<Real>("linear solver tolerance"));
228 :
229 710 : const unsigned int maxits = parameters.have_parameter<unsigned int>("linear solver maximum iterations") ?
230 0 : parameters.get<unsigned int>("linear solver maximum iterations") :
231 710 : es.parameters.get<unsigned int>("linear solver maximum iterations");
232 :
233 710 : const unsigned int nev = parameters.have_parameter<unsigned int>("eigenpairs") ?
234 0 : parameters.get<unsigned int>("eigenpairs") :
235 710 : es.parameters.get<unsigned int>("eigenpairs");
236 :
237 710 : const unsigned int ncv = parameters.have_parameter<unsigned int>("basis vectors") ?
238 0 : parameters.get<unsigned int>("basis vectors") :
239 359 : es.parameters.get<unsigned int>("basis vectors");
240 :
241 8 : std::pair<unsigned int, unsigned int> solve_data;
242 :
243 : // call the solver depending on the type of eigenproblem
244 :
245 359 : if (_use_shell_matrices)
246 : {
247 : // Generalized eigenproblem
248 66 : if (generalized())
249 : // Shell preconditioning matrix
250 66 : if (_use_shell_precond_matrix)
251 0 : solve_data = eigen_solver->solve_generalized(
252 0 : *shell_matrix_A, *shell_matrix_B, *shell_precond_matrix, nev, ncv, tol, maxits);
253 : else
254 66 : solve_data = eigen_solver->solve_generalized(
255 0 : *shell_matrix_A, *shell_matrix_B, *P, nev, ncv, tol, maxits);
256 :
257 : // Standard eigenproblem
258 : else
259 : {
260 0 : libmesh_assert(!shell_matrix_B);
261 : // Shell preconditioning matrix
262 0 : if (_use_shell_precond_matrix)
263 0 : solve_data = eigen_solver->solve_standard(
264 0 : *shell_matrix_A, *shell_precond_matrix, nev, ncv, tol, maxits);
265 : else
266 0 : solve_data = eigen_solver->solve_standard(*shell_matrix_A, *P, nev, ncv, tol, maxits);
267 : }
268 : }
269 : else
270 : {
271 : // Generalized eigenproblem
272 293 : if (generalized())
273 223 : solve_data = eigen_solver->solve_generalized(*A, *B, nev, ncv, tol, maxits);
274 :
275 : // Standard eigenproblem
276 : else
277 : {
278 2 : libmesh_assert(!matrix_B);
279 70 : solve_data = eigen_solver->solve_standard(*A, nev, ncv, tol, maxits);
280 : }
281 : }
282 :
283 359 : this->_n_converged_eigenpairs = solve_data.first;
284 359 : this->_n_iterations = solve_data.second;
285 359 : }
286 :
287 140 : void EigenSystem::solve ()
288 : {
289 140 : if (get_dof_map().n_constrained_dofs())
290 : libmesh_warning("EigenSystem does not have first-class support for constrained degrees of "
291 : "freedom. You may see spurious effects of the constrained degrees of freedom "
292 : "in a given eigenvector. If you wish to perform a reliable solve on a system "
293 : "with constraints, please use the CondensedEigenSystem class instead");
294 :
295 : // check that necessary parameters have been set
296 4 : libmesh_assert(
297 : this->get_equation_systems().parameters.have_parameter<unsigned int>("eigenpairs"));
298 4 : libmesh_assert(
299 : this->get_equation_systems().parameters.have_parameter<unsigned int>("basis vectors"));
300 :
301 140 : if (this->assemble_before_solve)
302 : // Assemble the linear system
303 140 : this->assemble ();
304 :
305 140 : solve_helper(matrix_A, matrix_B, precond_matrix);
306 140 : }
307 :
308 140 : std::pair<Real, Real> EigenSystem::get_eigenpair (dof_id_type i)
309 : {
310 : // call the eigen_solver get_eigenpair method
311 144 : return eigen_solver->get_eigenpair (i, *solution);
312 : }
313 :
314 0 : std::pair<Real, Real> EigenSystem::get_eigenvalue (dof_id_type i)
315 : {
316 0 : return eigen_solver->get_eigenvalue (i);
317 : }
318 :
319 0 : void EigenSystem::set_initial_space (NumericVector<Number> & initial_space_in)
320 : {
321 0 : eigen_solver->set_initial_space (initial_space_in);
322 0 : }
323 :
324 :
325 1006 : bool EigenSystem::generalized () const
326 : {
327 1036 : return _eigen_problem_type == GNHEP ||
328 1010 : _eigen_problem_type == GHEP ||
329 1010 : _eigen_problem_type == GHIEP;
330 : }
331 :
332 :
333 0 : const SparseMatrix<Number> & EigenSystem::get_matrix_A() const
334 : {
335 0 : libmesh_assert(matrix_A);
336 0 : libmesh_assert(!_use_shell_matrices);
337 0 : libmesh_assert(has_matrix_A());
338 0 : return *matrix_A;
339 : }
340 :
341 :
342 325 : SparseMatrix<Number> & EigenSystem::get_matrix_A()
343 : {
344 8 : libmesh_assert(matrix_A);
345 8 : libmesh_assert(!_use_shell_matrices);
346 8 : libmesh_assert(has_matrix_A());
347 325 : return *matrix_A;
348 : }
349 :
350 :
351 0 : const SparseMatrix<Number> & EigenSystem::get_matrix_B() const
352 : {
353 0 : libmesh_assert(matrix_B);
354 0 : libmesh_assert(!_use_shell_matrices);
355 0 : libmesh_assert(generalized());
356 0 : libmesh_assert(has_matrix_B());
357 0 : return *matrix_B;
358 : }
359 :
360 :
361 210 : SparseMatrix<Number> & EigenSystem::get_matrix_B()
362 : {
363 6 : libmesh_assert(matrix_B);
364 6 : libmesh_assert(!_use_shell_matrices);
365 6 : libmesh_assert(generalized());
366 6 : libmesh_assert(has_matrix_B());
367 210 : return *matrix_B;
368 : }
369 :
370 :
371 0 : const SparseMatrix<Number> & EigenSystem::get_precond_matrix() const
372 : {
373 0 : libmesh_assert(precond_matrix);
374 0 : libmesh_assert(_use_shell_matrices);
375 0 : libmesh_assert(!_use_shell_precond_matrix);
376 0 : libmesh_assert(has_precond_matrix());
377 0 : return *precond_matrix;
378 : }
379 :
380 :
381 264 : SparseMatrix<Number> & EigenSystem::get_precond_matrix()
382 : {
383 0 : libmesh_assert(precond_matrix);
384 0 : libmesh_assert(_use_shell_matrices);
385 0 : libmesh_assert(!_use_shell_precond_matrix);
386 0 : libmesh_assert(has_precond_matrix());
387 264 : return *precond_matrix;
388 : }
389 :
390 :
391 0 : const ShellMatrix<Number> & EigenSystem::get_shell_matrix_A() const
392 : {
393 0 : libmesh_assert(_use_shell_matrices);
394 0 : libmesh_assert(has_shell_matrix_A());
395 0 : return *shell_matrix_A;
396 : }
397 :
398 :
399 66 : ShellMatrix<Number> & EigenSystem::get_shell_matrix_A()
400 : {
401 0 : libmesh_assert(_use_shell_matrices);
402 0 : libmesh_assert(has_shell_matrix_A());
403 66 : return *shell_matrix_A;
404 : }
405 :
406 :
407 0 : const ShellMatrix<Number> & EigenSystem::get_shell_matrix_B() const
408 : {
409 0 : libmesh_assert(_use_shell_matrices);
410 0 : libmesh_assert(generalized());
411 0 : libmesh_assert(has_shell_matrix_B());
412 0 : return *shell_matrix_B;
413 : }
414 :
415 :
416 66 : ShellMatrix<Number> & EigenSystem::get_shell_matrix_B()
417 : {
418 0 : libmesh_assert(_use_shell_matrices);
419 0 : libmesh_assert(generalized());
420 0 : libmesh_assert(has_shell_matrix_B());
421 66 : return *shell_matrix_B;
422 : }
423 :
424 :
425 0 : const ShellMatrix<Number> & EigenSystem::get_shell_precond_matrix() const
426 : {
427 0 : libmesh_assert(_use_shell_matrices);
428 0 : libmesh_assert(_use_shell_precond_matrix);
429 0 : libmesh_assert(has_shell_precond_matrix());
430 0 : return *shell_precond_matrix;
431 : }
432 :
433 :
434 0 : ShellMatrix<Number> & EigenSystem::get_shell_precond_matrix()
435 : {
436 0 : libmesh_assert(_use_shell_matrices);
437 0 : libmesh_assert(_use_shell_precond_matrix);
438 0 : libmesh_assert(has_shell_precond_matrix());
439 0 : return *shell_precond_matrix;
440 : }
441 :
442 :
443 8 : bool EigenSystem::has_matrix_A() const
444 : {
445 8 : libmesh_assert_equal_to(request_matrix("Eigen Matrix A"), matrix_A);
446 8 : return matrix_A;
447 : }
448 :
449 :
450 6 : bool EigenSystem::has_matrix_B() const
451 : {
452 6 : libmesh_assert_equal_to(request_matrix("Eigen Matrix B"), matrix_B);
453 6 : return matrix_B;
454 : }
455 :
456 :
457 0 : bool EigenSystem::has_precond_matrix() const
458 : {
459 0 : libmesh_assert_equal_to(request_matrix("Eigen Preconditioner"), precond_matrix);
460 0 : return precond_matrix;
461 : }
462 :
463 :
464 0 : bool EigenSystem::has_shell_matrix_A() const
465 : {
466 0 : return shell_matrix_A.get();
467 : }
468 :
469 :
470 0 : bool EigenSystem::has_shell_matrix_B() const
471 : {
472 0 : return shell_matrix_B.get();
473 : }
474 :
475 :
476 0 : bool EigenSystem::has_shell_precond_matrix() const
477 : {
478 0 : return shell_precond_matrix.get();
479 : }
480 :
481 :
482 0 : const EigenSolver<Number> & EigenSystem::get_eigen_solver() const
483 : {
484 0 : libmesh_assert(eigen_solver);
485 0 : return *eigen_solver;
486 : }
487 :
488 :
489 412 : EigenSolver<Number> & EigenSystem::get_eigen_solver()
490 : {
491 8 : libmesh_assert(eigen_solver);
492 412 : return *eigen_solver;
493 : }
494 :
495 :
496 : } // namespace libMesh
497 :
498 : #endif // LIBMESH_HAVE_SLEPC
|