libMesh
Loading...
Searching...
No Matches
eigenproblems_ex4.C
Go to the documentation of this file.
1// The libMesh Finite Element Library.
2// Copyright (C) 2002-2026 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// <h1>Eigenproblems Example 4 - Constrained Eigen Problem Solved with Newton Method</h1>
19// \author Alexander Lindsay
20// \date 2024
21//
22// This example illustrates how to solve an eigen problem with constraints from hanging
23// nodes using Newton's method, which is implemented in SLEPc as a variant of the power method.
24// Unlike for the standard power method, the Bx function is computed using the current solution.
25// This example will only work if the library is compiled with SLEPc support enabled.
26//
27// The Ax function is composed of diffusion with vacuum boundary conditions (-n * grad u
28// = u). The Bx function corresponds to a mass term
29
30// libMesh include files.
31#include "libmesh/libmesh.h"
32#include "libmesh/mesh.h"
33#include "libmesh/mesh_generation.h"
34#include "libmesh/exodusII_io.h"
35#include "libmesh/condensed_eigen_system.h"
36#include "libmesh/equation_systems.h"
37#include "libmesh/fe.h"
38#include "libmesh/quadrature_gauss.h"
39#include "libmesh/dense_matrix.h"
40#include "libmesh/petsc_matrix.h"
41#include "libmesh/petsc_vector.h"
42#include "libmesh/dof_map.h"
43#include "libmesh/getpot.h"
44#include "libmesh/eigen_solver.h"
45#include "libmesh/enum_eigen_solver_type.h"
46#include "libmesh/petsc_shell_matrix.h"
47#include "libmesh/elem.h"
48#include "libmesh/mesh_refinement.h"
49#include "libmesh/slepc_macro.h"
50
51// Bring in everything from the libMesh namespace
52using namespace libMesh;
53
54#ifdef LIBMESH_HAVE_SLEPC
55#include <petscsnes.h>
56
57PetscErrorCode form_functionA(SNES snes, Vec x, Vec Ax, void * ctx);
58PetscErrorCode form_matrixA(SNES snes, Vec x, Mat jac, Mat pc, void * ctx);
59PetscErrorCode form_functionB(SNES snes, Vec x, Vec Bx, void * ctx);
60#endif
61
62int
63main(int argc, char ** argv)
64{
65 // Initialize libMesh and the dependent libraries.
66 LibMeshInit init(argc, argv);
67
68#ifdef LIBMESH_DEFAULT_SINGLE_PRECISION
69 // SLEPc currently gives us a nasty crash with Real==float
70 libmesh_example_requires(false, "--disable-singleprecision");
71#endif
72
73#ifndef LIBMESH_HAVE_SLEPC
74 libmesh_example_requires(false, "--enable-slepc with a slepc version >=3.13");
75#else
76#if SLEPC_VERSION_LESS_THAN(3, 13, 0)
77 libmesh_example_requires(false, "--enable-slepc with a slepc version >=3.13");
78#else
79 // Tell the user what we are doing.
80 libMesh::out << "Running " << argv[0];
81
82 for (int i = 1; i < argc; i++)
83 libMesh::out << " " << argv[i];
84
85 libMesh::out << std::endl << std::endl;
86
87 // We only solve for a single eigenvalue with the nonlinear power method
88 constexpr int nev = 1;
89
90 // Possibly get the mesh size from -nx and -ny
91 const int nx = libMesh::command_line_next("-nx", 20), ny = libMesh::command_line_next("-ny", 20);
92
93 // Skip this 2D example if libMesh was compiled as 1D-only.
94 libmesh_example_requires(2 <= LIBMESH_DIM, "2D support");
95
96 // Create a mesh, with dimension to be overridden later, on the
97 // default MPI communicator.
98 Mesh mesh(init.comm());
99
100 // Use the internal mesh generator to create a uniform
101 // 2D grid on a square.
102 MeshTools::Generation::build_square(mesh, nx, ny, -1., 1., -1., 1., QUAD4);
103
104#ifdef LIBMESH_ENABLE_AMR
105 for (auto * elem : mesh.active_element_ptr_range())
106 if (elem->vertex_average()(0) < 0)
107 elem->set_refinement_flag(Elem::REFINE);
108
109 MeshRefinement refinedmesh(mesh);
110 refinedmesh.refine_elements();
111#endif
112
113 // Print information about the mesh to the screen.
115
116 // Create an equation systems object.
117 EquationSystems equation_systems(mesh);
118
119 // Create a CondensedEigenSystem named "Eigensystem" and (for convenience)
120 // use a reference to the system we create.
121 auto & eigen_system = equation_systems.add_system<CondensedEigenSystem>("Condensed Eigensystem");
122
123 // Declare the system variables.
124 // Adds the variable "p" to "Eigensystem". "p"
125 // will be approximated using second-order approximation.
126 eigen_system.add_variable("p", FIRST);
127
128 // Set necessary parameters used in EigenSystem::solve(),
129 // i.e. the number of requested eigenpairs nev and the number
130 // of basis vectors ncv used in the solution algorithm. Note that
131 // ncv >= nev must hold and ncv >= 2*nev is recommended.
132 equation_systems.parameters.set<unsigned int>("eigenpairs") = nev;
133 equation_systems.parameters.set<unsigned int>("basis vectors") = nev * 3;
134
135 eigen_system.set_eigenproblem_type(GNHEP);
136 eigen_system.use_shell_matrices(true);
137
138 // Initialize the data structures for the equation system.
139 equation_systems.init();
140
141 // Prints information about the system to the screen.
142 equation_systems.print_info();
143
144 LibmeshPetscCallQ(PetscOptionsSetValue(LIBMESH_PETSC_NULLPTR, "-eps_type", "power"));
145 LibmeshPetscCallQ(PetscOptionsSetValue(LIBMESH_PETSC_NULLPTR, "-eps_power_update", "1"));
146 LibmeshPetscCallQ(PetscOptionsSetValue(LIBMESH_PETSC_NULLPTR, "-eps_power_nonlinear", "1"));
147 LibmeshPetscCallQ(PetscOptionsSetValue(LIBMESH_PETSC_NULLPTR, "-eps_max_it", "1"));
148 LibmeshPetscCallQ(PetscOptionsSetValue(LIBMESH_PETSC_NULLPTR, "-eps_power_snes_mf_operator", "1"));
149 LibmeshPetscCallQ(PetscOptionsSetValue(LIBMESH_PETSC_NULLPTR, "-eps_power_pc_type", "lu"));
150
151 //
152 // Set function/operator callback functions
153 //
154
155 auto & A = static_cast<PetscShellMatrix<Number> &>(eigen_system.get_shell_matrix_A());
156 auto Amat = A.mat();
157 LibmeshPetscCallQ(PetscObjectComposeFunction((PetscObject)Amat, "formFunction", form_functionA));
158 LibmeshPetscCallQ(PetscObjectComposeFunction((PetscObject)Amat, "formJacobian", form_matrixA));
159
160 auto & B = static_cast<PetscShellMatrix<Number> &>(eigen_system.get_shell_matrix_B());
161 auto Bmat = B.mat();
162 LibmeshPetscCallQ(PetscObjectComposeFunction((PetscObject)Bmat, "formFunction", form_functionB));
163
164 //
165 // Set function/operator callback function contexts
166 //
167
168 PetscContainer container;
169 LibmeshPetscCallQ(PetscContainerCreate(equation_systems.comm().get(), &container));
170 LibmeshPetscCallQ(PetscContainerSetPointer(container, &equation_systems));
171 LibmeshPetscCallQ(PetscObjectCompose((PetscObject)Amat, "formFunctionCtx", (PetscObject)container));
172 LibmeshPetscCallQ(PetscObjectCompose((PetscObject)Amat, "formJacobianCtx", (PetscObject)container));
173 LibmeshPetscCallQ(PetscObjectCompose((PetscObject)Bmat, "formFunctionCtx", (PetscObject)container));
174 LibmeshPetscCallQ(PetscContainerDestroy(&container));
175
176 // Set the initial space
177 Vec initial_space;
178 LibmeshPetscCallQ(MatCreateVecs(Amat, &initial_space, nullptr));
179 PetscVector<Number> wrapped_initial_space(initial_space, equation_systems.comm());
180 wrapped_initial_space.add(1);
181 wrapped_initial_space.close();
182 eigen_system.get_eigen_solver().set_initial_space(wrapped_initial_space);
183
184 // Solve the system "Condensed Eigensystem"
185 eigen_system.initialize_condensed_matrices();
186 eigen_system.dont_create_submatrices_in_solve();
187 eigen_system.assemble_before_solve = false;
188 eigen_system.get_eigen_solver().set_close_matrix_before_solve(false);
189 eigen_system.solve();
190
191 // Get the number of converged eigen pairs.
192 unsigned int nconv = eigen_system.get_n_converged();
193
194 // Get the last converged eigenpair
195 if (nconv != 0)
196 {
197 auto [re, im] = eigen_system.get_eigenpair(0);
198 libMesh::out << "The converged eigenvalue is " << re << " + " << im << "i" << std::endl;
199
200#ifdef LIBMESH_HAVE_EXODUS_API
201 // Write the eigen vector to file.
202 ExodusII_IO(mesh).write_equation_systems("out.e", equation_systems);
203#endif // #ifdef LIBMESH_HAVE_EXODUS_API
204 }
205 else
206 libMesh::out << "WARNING: Solver did not converge!\n" << nconv << std::endl;
207
208 LibmeshPetscCallQ(VecDestroy(&initial_space));
209
210 // All done.
211 return 0;
212#endif // SLEPC version >= 3.13.0
213#endif // LIBMESH_HAVE_SLEPC
214}
215
216#ifdef LIBMESH_HAVE_SLEPC
217void
219{
220 auto & dof_map = sys.get_dof_map();
221
222 PetscVector<Number> X_global(x, sys.comm());
223
224 if (dof_map.n_constrained_dofs())
225 {
226 sys.copy_sub_to_super(X_global, *sys.solution);
227 // Set the constrained dof values
228 dof_map.enforce_constraints_exactly(sys);
229 sys.update();
230 }
231 else
232 {
233 PetscVector<Number> & X_sys = *cast_ptr<PetscVector<Number> *>(sys.solution.get());
234
235 // Use the system's update() to get a good local version of the
236 // parallel solution. This operation does not modify the incoming
237 // "x" vector, it only localizes information from "x" into
238 // sys.current_local_solution.
239 X_global.swap(X_sys);
240 sys.update();
241 X_global.swap(X_sys);
242 }
243}
244
245std::unique_ptr<NumericVector<Number>>
247{
248 auto & dof_map = sys.get_dof_map();
249
250 if (dof_map.n_constrained_dofs())
251 return sys.solution->zero_clone();
252 else
253 {
254 auto F = std::make_unique<PetscVector<Number>>(f, sys.comm());
255 F->zero();
256 return F;
257 }
258}
259
260PetscErrorCode
261form_functionA(SNES /*snes*/, Vec x, Vec Ax, void * ctx)
262{
263 PetscFunctionBegin;
264
265 auto & es = *static_cast<EquationSystems *>(ctx);
266
267 // Get a reference to our system.
268 auto & eigen_system = es.get_system<CondensedEigenSystem>("Condensed Eigensystem");
269
270 // A reference to the DofMap object for this system. The DofMap
271 // object handles the index translation from node and element numbers
272 // to degree of freedom numbers.
273 auto & dof_map = eigen_system.get_dof_map();
274
275 // Create our data structures correponding to both constrained and unconstrained degrees of
276 // freedom
277 update_current_local_solution(eigen_system, x);
278 auto AX = create_wrapped_function(eigen_system, Ax);
279
280 // Get a constant reference to the mesh object.
281 const MeshBase & mesh = es.get_mesh();
282
283 // The dimension that we are running.
284 const unsigned int dim = mesh.mesh_dimension();
285
286 // Get a constant reference to the Finite Element type
287 // for the first (and only) variable in the system.
288 FEType fe_type = dof_map.variable_type(0);
289
290 // Build a Finite Element object of the specified type. Since the
291 // FEBase::build() member dynamically creates memory we will
292 // store the object as a std::unique_ptr<FEBase>. This can be thought
293 // of as a pointer that will clean up after itself.
294 std::unique_ptr<FEBase> fe(FEBase::build(dim, fe_type));
295
296 // A Gauss quadrature rule for numerical integration.
297 // Use the default quadrature order.
298 QGauss qrule(dim, fe_type.default_quadrature_order());
299
300 // Tell the finite element object to use our quadrature rule.
301 fe->attach_quadrature_rule(&qrule);
302
303 // Do the same for faces
304 std::unique_ptr<FEBase> fe_face(FEBase::build(dim, fe_type));
305 QGauss qrule_face(dim - 1, fe_type.default_quadrature_order());
306 fe_face->attach_quadrature_rule(&qrule_face);
307
308 // The element Jacobian * quadrature weight at each integration point.
309 const std::vector<Real> & JxW = fe->get_JxW();
310
311 // The element shape functions evaluated at the quadrature points.
312 const auto & dphi = fe->get_dphi();
313
314 // The element degree of freedom values
315 std::vector<Number> Ue;
316
317 // The diffusion residual
319
320 // This vector will hold the degree of freedom indices for
321 // the element. These define where in the global system
322 // the element degrees of freedom get mapped.
323 std::vector<dof_id_type> dof_indices;
324
325 // Now we will loop over all the elements in the mesh that
326 // live on the local processor. We will compute the element
327 // matrix and right-hand-side contribution. In case users
328 // later modify this program to include refinement, we will
329 // be safe and will only consider the active elements;
330 // hence we use a variant of the active_elem_iterator.
331 for (const auto & elem : mesh.active_local_element_ptr_range())
332 {
333 // Get the degree of freedom indices for the
334 // current element. These define where in the global
335 // matrix and right-hand-side this element will
336 // contribute to.
337 dof_map.dof_indices(elem, dof_indices);
338
339 // Compute the element-specific data for the current
340 // element. This involves computing the location of the
341 // quadrature points (q_point) and the shape functions
342 // (phi, dphi) for the current element.
343 fe->reinit(elem);
344
345 // Get the element degree of freedom values
346 eigen_system.current_local_solution->get(dof_indices, Ue);
347
348 // Zero the element matrices and rhs before
349 // summing them. We use the resize member here because
350 // the number of degrees of freedom might have changed from
351 // the last element. Note that this will be the case if the
352 // element type is different (i.e. the last element was a
353 // triangle, now we are on a quadrilateral).
354 const unsigned int n_dofs = cast_int<unsigned int>(dof_indices.size());
355 Ke.resize(n_dofs);
356
357 // Now loop over the quadrature points. This handles
358 // the numeric integration.
359 //
360 // We will build the element vector
361 for (const auto qp : make_range(qrule.n_points()))
362 {
363 // Build the solution at the quadrature point
364 Gradient grad_u_qp = 0;
365 for (const auto i : make_range(n_dofs))
366 grad_u_qp += dphi[i][qp] * Ue[i];
367 // Now compute the action of the mass operator on the solution
368 for (const auto i : make_range(n_dofs))
369 Ke(i) += JxW[qp] * dphi[i][qp] * grad_u_qp;
370 }
371
372 for (const auto s : elem->side_index_range())
373 if (!elem->neighbor_ptr(s))
374 {
375 const auto & phi_face = fe_face->get_phi();
376 const auto & JxW_face = fe_face->get_JxW();
377 // vacuum boundary condition
378 fe_face->reinit(elem, s);
379 for (const auto qp : make_range(qrule_face.n_points()))
380 {
381 Number u_qp = 0;
382 for (const auto i : make_range(n_dofs))
383 u_qp += phi_face[i][qp] * Ue[i];
384 for (const auto i : make_range(n_dofs))
385 Ke(i) += JxW_face[qp] * phi_face[i][qp] * u_qp;
386 }
387 }
388
389 // On an unrefined mesh, constrain_element_vector does
390 // nothing. If the assembly function is
391 // run on a refined mesh, getting the hanging node constraints
392 // right will be important
393 dof_map.constrain_element_vector(Ke, dof_indices);
394
395 // Finally, simply add the element contribution to the
396 // overall matrix.
397 AX->add_vector(Ke, dof_indices);
398 } // end of element loop
399
400 AX->close();
401
402 if (dof_map.n_constrained_dofs())
403 {
404 PetscVector<Number> wrapped_Ax(Ax, eigen_system.comm());
405 eigen_system.copy_super_to_sub(*AX, wrapped_Ax);
406 }
407
408 PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
409}
410
411PetscErrorCode
412form_functionB(SNES /*snes*/, Vec x, Vec Bx, void * ctx)
413{
414 PetscFunctionBegin;
415
416 auto & es = *static_cast<EquationSystems *>(ctx);
417
418 // Get a reference to our system.
419 auto & eigen_system = es.get_system<CondensedEigenSystem>("Condensed Eigensystem");
420
421 // A reference to the DofMap object for this system. The DofMap
422 // object handles the index translation from node and element numbers
423 // to degree of freedom numbers.
424 auto & dof_map = eigen_system.get_dof_map();
425
426 // Create our data structures correponding to both constrained and unconstrained degrees of
427 // freedom
428 update_current_local_solution(eigen_system, x);
429 auto BX = create_wrapped_function(eigen_system, Bx);
430
431 // Get a constant reference to the mesh object.
432 const MeshBase & mesh = es.get_mesh();
433
434 // The dimension that we are running.
435 const unsigned int dim = mesh.mesh_dimension();
436
437 // Get a constant reference to the Finite Element type
438 // for the first (and only) variable in the system.
439 FEType fe_type = dof_map.variable_type(0);
440
441 // Build a Finite Element object of the specified type. Since the
442 // FEBase::build() member dynamically creates memory we will
443 // store the object as a std::unique_ptr<FEBase>. This can be thought
444 // of as a pointer that will clean up after itself.
445 std::unique_ptr<FEBase> fe(FEBase::build(dim, fe_type));
446
447 // A Gauss quadrature rule for numerical integration.
448 // Use the default quadrature order.
449 QGauss qrule(dim, fe_type.default_quadrature_order());
450
451 // Tell the finite element object to use our quadrature rule.
452 fe->attach_quadrature_rule(&qrule);
453
454 // The element Jacobian * quadrature weight at each integration point.
455 const std::vector<Real> & JxW = fe->get_JxW();
456
457 // The element shape functions evaluated at the quadrature points.
458 const std::vector<std::vector<Real>> & phi = fe->get_phi();
459
460 // The element degree of freedom values
461 std::vector<Number> Ue;
462
463 // The element mass matrix residual
465
466 // This vector will hold the degree of freedom indices for
467 // the element. These define where in the global system
468 // the element degrees of freedom get mapped.
469 std::vector<dof_id_type> dof_indices;
470
471 // Now we will loop over all the elements in the mesh that
472 // live on the local processor. We will compute the element
473 // matrix and right-hand-side contribution. In case users
474 // later modify this program to include refinement, we will
475 // be safe and will only consider the active elements;
476 // hence we use a variant of the active_elem_iterator.
477 for (const auto & elem : mesh.active_local_element_ptr_range())
478 {
479 // Get the degree of freedom indices for the
480 // current element. These define where in the global
481 // matrix and right-hand-side this element will
482 // contribute to.
483 dof_map.dof_indices(elem, dof_indices);
484
485 // Compute the element-specific data for the current
486 // element. This involves computing the location of the
487 // quadrature points (q_point) and the shape functions
488 // (phi, dphi) for the current element.
489 fe->reinit(elem);
490
491 // Get the element degree of freedom values
492 eigen_system.current_local_solution->get(dof_indices, Ue);
493
494 // Zero the element matrices and rhs before
495 // summing them. We use the resize member here because
496 // the number of degrees of freedom might have changed from
497 // the last element. Note that this will be the case if the
498 // element type is different (i.e. the last element was a
499 // triangle, now we are on a quadrilateral).
500 const unsigned int n_dofs = cast_int<unsigned int>(dof_indices.size());
501 Me.resize(n_dofs);
502
503 // Now loop over the quadrature points. This handles
504 // the numeric integration.
505 //
506 // We will build the element vector
507 for (unsigned int qp = 0; qp < qrule.n_points(); qp++)
508 {
509 // Build the solution at the quadrature point
510 Number Uqp = 0;
511 for (const auto i : make_range(n_dofs))
512 Uqp += phi[i][qp] * Ue[i];
513 // Now compute the action of the mass operator on the solution
514 for (unsigned int i = 0; i != n_dofs; i++)
515 Me(i) += JxW[qp] * phi[i][qp] * Uqp;
516 }
517
518 // On an unrefined mesh, constrain_element_vector does
519 // nothing. If the assembly function is
520 // run on a refined mesh, getting the hanging node constraints
521 // right will be important
522 dof_map.constrain_element_vector(Me, dof_indices);
523
524 // Finally, simply add the element contribution to the
525 // overall matrix.
526 BX->add_vector(Me, dof_indices);
527 } // end of element loop
528
529 BX->close();
530
531 if (dof_map.n_constrained_dofs())
532 {
533 PetscVector<Number> wrapped_Bx(Bx, eigen_system.comm());
534 eigen_system.copy_super_to_sub(*BX, wrapped_Bx);
535 }
536
537 PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
538}
539
540PetscErrorCode
541form_matrixA(SNES /*snes*/, Vec x, Mat jac, Mat pc, void * ctx)
542{
543 PetscFunctionBegin;
544
545 auto & es = *static_cast<EquationSystems *>(ctx);
546
547 // Get a reference to our system.
548 auto & eigen_system = es.get_system<CondensedEigenSystem>("Condensed Eigensystem");
549
550 // A reference to the DofMap object for this system. The DofMap
551 // object handles the index translation from node and element numbers
552 // to degree of freedom numbers.
553 auto & dof_map = eigen_system.get_dof_map();
554
555 //
556 // Create our data structures correponding to both constrained and unconstrained degrees of
557 // freedom
558 //
559
560 update_current_local_solution(eigen_system, x);
561
562 PetscBool pisshell, jismffd;
563
564 LibmeshPetscCallQ(PetscObjectTypeCompare((PetscObject)pc, MATSHELL, &pisshell));
565 if (pisshell)
566 libmesh_error_msg("Generic preconditioning requires that an explicit matrix representation of "
567 "the preconditioner be formed");
568 LibmeshPetscCallQ(PetscObjectTypeCompare((PetscObject)jac, MATMFFD, &jismffd));
569 if (!jismffd)
570 libmesh_error_msg("The operator should be formed matrix free");
571
572 auto & pc_super = eigen_system.get_precond_matrix();
573
574 // Get a constant reference to the mesh object.
575 const MeshBase & mesh = es.get_mesh();
576
577 // The dimension that we are running.
578 const unsigned int dim = mesh.mesh_dimension();
579
580 // Get a constant reference to the Finite Element type
581 // for the first (and only) variable in the system.
582 FEType fe_type = dof_map.variable_type(0);
583
584 // Build a Finite Element object of the specified type. Since the
585 // FEBase::build() member dynamically creates memory we will
586 // store the object as a std::unique_ptr<FEBase>. This can be thought
587 // of as a pointer that will clean up after itself.
588 std::unique_ptr<FEBase> fe(FEBase::build(dim, fe_type));
589
590 // A Gauss quadrature rule for numerical integration.
591 // Use the default quadrature order.
592 QGauss qrule(dim, fe_type.default_quadrature_order());
593
594 // Tell the finite element object to use our quadrature rule.
595 fe->attach_quadrature_rule(&qrule);
596
597 // Do the same for faces
598 std::unique_ptr<FEBase> fe_face(FEBase::build(dim, fe_type));
599 QGauss qrule_face(dim - 1, fe_type.default_quadrature_order());
600 fe_face->attach_quadrature_rule(&qrule_face);
601
602 // The element Jacobian * quadrature weight at each integration point.
603 const std::vector<Real> & JxW = fe->get_JxW();
604
605 // The element shape function gradients evaluated at the quadrature points.
606 const auto & dphi = fe->get_dphi();
607
608 // The element diffusion matrix
610
611 // This vector will hold the degree of freedom indices for
612 // the element. These define where in the global system
613 // the element degrees of freedom get mapped.
614 std::vector<dof_id_type> dof_indices;
615
616 // Now we will loop over all the elements in the mesh that
617 // live on the local processor. We will compute the element
618 // matrix and right-hand-side contribution. In case users
619 // later modify this program to include refinement, we will
620 // be safe and will only consider the active elements;
621 // hence we use a variant of the active_elem_iterator.
622 for (const auto & elem : mesh.active_local_element_ptr_range())
623 {
624 // Get the degree of freedom indices for the
625 // current element. These define where in the global
626 // matrix and right-hand-side this element will
627 // contribute to.
628 dof_map.dof_indices(elem, dof_indices);
629
630 // Compute the element-specific data for the current
631 // element. This involves computing the location of the
632 // quadrature points (q_point) and the shape functions
633 // (phi, dphi) for the current element.
634 fe->reinit(elem);
635
636 // Zero the element matrices and rhs before
637 // summing them. We use the resize member here because
638 // the number of degrees of freedom might have changed from
639 // the last element. Note that this will be the case if the
640 // element type is different (i.e. the last element was a
641 // triangle, now we are on a quadrilateral).
642 const unsigned int n_dofs = cast_int<unsigned int>(dof_indices.size());
643 Ke.resize(n_dofs, n_dofs);
644
645 // Now loop over the quadrature points. This handles
646 // the numeric integration.
647 //
648 // We will build the element matrix
649 for (const auto qp : make_range(qrule.n_points()))
650 // Now compute the mass operator
651 for (const auto i : make_range(n_dofs))
652 for (const auto j : make_range(n_dofs))
653 Ke(i, j) += JxW[qp] * dphi[i][qp] * dphi[j][qp];
654
655 for (const auto s : elem->side_index_range())
656 if (!elem->neighbor_ptr(s))
657 {
658 const auto & phi_face = fe_face->get_phi();
659 const auto & JxW_face = fe_face->get_JxW();
660 // vacuum boundary condition
661 fe_face->reinit(elem, s);
662 for (const auto qp : make_range(qrule_face.n_points()))
663 {
664 for (const auto i : make_range(n_dofs))
665 for (const auto j : make_range(n_dofs))
666 Ke(i, j) += JxW_face[qp] * phi_face[i][qp] * phi_face[j][qp];
667 }
668 }
669
670 // On an unrefined mesh, constrain_element_vector does
671 // nothing. If the assembly function is
672 // run on a refined mesh, getting the hanging node constraints
673 // right will be important
674 dof_map.constrain_element_matrix(Ke, dof_indices);
675
676 // Finally, simply add the element contribution to the
677 // overall matrix.
678 pc_super.add_matrix(Ke, dof_indices, dof_indices);
679 } // end of element loop
680
681 pc_super.close();
682
683 if (dof_map.n_constrained_dofs())
684 {
685 // We create our libmesh matrix context using PetscObjectCompose, but versions of MatHeaderMerge
686 // in PETSc prior to 3.21.0 erase the composition list
687#if SLEPC_VERSION_LESS_THAN(3,21,0)
688 PetscMatrix<Number> sub(pc, eigen_system.comm());
689 eigen_system.copy_super_to_sub(pc_super, sub);
690#else
691 auto sub = PetscMatrixBase<Number>::get_context(pc, eigen_system.comm());
692 libmesh_assert(sub);
693 eigen_system.copy_super_to_sub(pc_super, *sub);
694#endif
695 }
696
697 // The MFFD Jac still must have assemble called on it
698 LibmeshPetscCallQ(MatAssemblyBegin(jac, MAT_FINAL_ASSEMBLY));
699 LibmeshPetscCallQ(MatAssemblyEnd(jac, MAT_FINAL_ASSEMBLY));
700
701 PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
702}
703
704#endif
unsigned int dim
This class extends EigenSystem to allow a simple way of solving (standard or generalized) eigenvalue ...
void copy_sub_to_super(const NumericVector< Number > &sub, NumericVector< Number > &super)
Copy a logically sub-vector into a super-vector.
virtual void reinit() override
Reinitializes the member data fields associated with the system, so that, e.g., assemble() may be use...
Defines a dense matrix for use in Finite Element-type computations.
void resize(const unsigned int new_m, const unsigned int new_n)
Resizes the matrix to the specified size and calls zero().
Defines a dense vector for use in Finite Element-type computations.
void resize(const unsigned int n)
Resize the vector.
This is the EquationSystems class.
void print_info(std::ostream &os=libMesh::out) const
Prints information about the equation systems, by default to libMesh::out.
Parameters parameters
Data structure holding arbitrary parameters.
virtual void init()
Initialize all the systems.
virtual System & add_system(std::string_view system_type, std::string_view name)
Add the system of type system_type named name to the systems array.
const T_sys & get_system(std::string_view name) const
The ExodusII_IO class implements reading meshes in the ExodusII file format from Sandia National Labs...
Definition exodusII_io.h:53
virtual void write_equation_systems(const std::string &fname, const EquationSystems &es, const std::set< std::string > *system_names=nullptr) override
Writes out the solution for no specific time or timestep.
static std::unique_ptr< FEGenericBase > build(const unsigned int dim, const FEType &type)
Builds a specific finite element type.
class FEType hides (possibly multiple) FEFamily and approximation orders, thereby enabling specialize...
Definition fe_type.h:197
Order default_quadrature_order() const
Definition fe_type.h:415
The LibMeshInit class, when constructed, initializes the dependent libraries (e.g.
Definition libmesh.h:92
This is the MeshBase class.
Definition mesh_base.h:81
unsigned int mesh_dimension() const
Definition mesh_base.C:430
void print_info(std::ostream &os=libMesh::out, const unsigned int verbosity=0, const bool global=true) const
Prints relevant information about the mesh.
Definition mesh_base.C:1755
Implements (adaptive) mesh refinement algorithms for a MeshBase.
bool refine_elements()
Only refines the user-requested elements.
The Mesh class is a thin wrapper, around the ReplicatedMesh class by default.
Definition mesh.h:51
virtual void close()=0
Calls the NumericVector's internal assembly routines, ensuring that the values are consistent across ...
const Parallel::Communicator & comm() const
T & set(const std::string &)
Definition parameters.h:494
static PetscMatrixBase< T > * get_context(Mat mat, const TIMPI::Communicator &comm)
This class provides a nice interface to the PETSc C-based AIJ data structures for parallel,...
This class allows to use a PETSc shell matrix.
Mat mat()
Returns a pointer to the underlying PETSc Mat object.
This class provides a nice interface to PETSc's Vec object.
virtual void close() override
Calls the NumericVector's internal assembly routines, ensuring that the values are consistent across ...
virtual void swap(NumericVector< T > &v) override
Swaps the contents of this with v.
virtual void add(const numeric_index_type i, const T value) override
Adds value to the vector entry specified by i.
unsigned int n_points() const
Definition quadrature.h:131
This class implements specific orders of Gauss quadrature.
virtual void close()=0
Calls the SparseMatrix's internal assembly routines, ensuring that the values are consistent across p...
SparseMatrix< Number > & add_matrix(std::string_view mat_name, ParallelType type=PARALLEL, MatrixBuildType mat_build_type=MatrixBuildType::AUTOMATIC)
Adds the additional matrix mat_name to this system.
Definition system.C:998
const FEType & variable_type(const unsigned int i) const
Definition system.C:2721
unsigned int add_variable(std::string_view var, const FEType &type, const std::set< subdomain_id_type > *const active_subdomains=nullptr)
Adds the variable var to the list of variables for this system.
Definition system.C:1344
NumericVector< Number > & add_vector(std::string_view vec_name, const bool projections=true, const ParallelType type=PARALLEL)
Adds the additional vector vec_name to this system.
Definition system.C:756
dof_id_type n_constrained_dofs() const
Definition system.C:125
std::unique_ptr< NumericVector< Number > > solution
Data structure to hold solution values.
Definition system.h:1655
virtual void update()
Update the local values to reflect the solution on neighboring processors.
Definition system.C:498
const DofMap & get_dof_map() const
Definition system.h:2417
This class defines a vector in LIBMESH_DIM dimensional Real or Complex space.
void update_current_local_solution(CondensedEigenSystem &sys, Vec x)
PetscErrorCode form_matrixA(SNES snes, Vec x, Mat jac, Mat pc, void *ctx)
std::unique_ptr< NumericVector< Number > > create_wrapped_function(CondensedEigenSystem &sys, Vec f)
PetscErrorCode form_functionA(SNES snes, Vec x, Vec Ax, void *ctx)
PetscErrorCode form_functionB(SNES snes, Vec x, Vec Bx, void *ctx)
MeshBase & mesh
void build_square(UnstructuredMesh &mesh, const unsigned int nx, const unsigned int ny, const Real xmin=0., const Real xmax=1., const Real ymin=0., const Real ymax=1., const ElemType type=INVALID_ELEM, const bool gauss_lobatto_grid=false)
A specialized build_cube() for 2D meshes.
The libMesh namespace provides an interface to certain functionality in the library.
PetscFunctionReturn(LIBMESH_PETSC_SUCCESS)
libmesh_assert(ctx)
OStreamProxy out
T command_line_next(std::string name, T default_value)
Use GetPot's search()/next() functions to get following arguments from the command line.
Definition libmesh.C:1025
LibmeshPetscCallQ(DMShellGetContext(dm, &ctx))
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition int_range.h:176
Definition assembly.h:39
int main()