libMesh
eigenproblems_ex4.C
Go to the documentation of this file.
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 // <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
52 using namespace libMesh;
53 
54 #ifdef LIBMESH_HAVE_SLEPC
55 #include <petscsnes.h>
56 
57 PetscErrorCode form_functionA(SNES snes, Vec x, Vec Ax, void * ctx);
58 PetscErrorCode form_matrixA(SNES snes, Vec x, Mat jac, Mat pc, void * ctx);
59 PetscErrorCode form_functionB(SNES snes, Vec x, Vec Bx, void * ctx);
60 #endif
61 
62 int
63 main(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.
114  mesh.print_info();
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
217 void
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 
245 std::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 
260 PetscErrorCode
261 form_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 
411 PetscErrorCode
412 form_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 
540 PetscErrorCode
541 form_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
class FEType hides (possibly multiple) FEFamily and approximation orders, thereby enabling specialize...
Definition: fe_type.h:196
T command_line_next(std::string name, T default_value)
Use GetPot&#39;s search()/next() functions to get following arguments from the command line...
Definition: libmesh.C:1078
This is the EquationSystems class.
This class provides a nice interface to PETSc&#39;s Vec object.
Definition: petsc_vector.h:73
unsigned int dim
The ExodusII_IO class implements reading meshes in the ExodusII file format from Sandia National Labs...
Definition: exodusII_io.h:52
bool refine_elements()
Only refines the user-requested elements.
Mat mat()
Returns a pointer to the underlying PETSc Mat object.
void resize(const unsigned int n)
Resize the vector.
Definition: dense_vector.h:396
virtual void add(const numeric_index_type i, const T value) override
Adds value to the vector entry specified by i.
Definition: petsc_vector.C:165
This class allows to use a PETSc shell matrix.
void print_info(std::ostream &os=libMesh::out) const
Prints information about the equation systems, by default to libMesh::out.
MeshBase & mesh
const Parallel::Communicator & comm() const
Order default_quadrature_order() const
Definition: fe_type.h:371
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:768
static PetscMatrixBase< T > * get_context(Mat mat, const TIMPI::Communicator &comm)
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.
This class defines a vector in LIBMESH_DIM dimensional Real or Complex space.
The LibMeshInit class, when constructed, initializes the dependent libraries (e.g.
Definition: libmesh.h:90
The libMesh namespace provides an interface to certain functionality in the library.
PetscErrorCode form_functionB(SNES snes, Vec x, Vec Bx, void *ctx)
const T_sys & get_system(std::string_view name) const
This is the MeshBase class.
Definition: mesh_base.h:75
virtual void swap(NumericVector< T > &v) override
Swaps the contents of this with v.
Definition: assembly.h:38
Implements (adaptive) mesh refinement algorithms for a MeshBase.
void copy_sub_to_super(const NumericVector< Number > &sub, NumericVector< Number > &super)
Copy a logically sub-vector into a super-vector.
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.
Definition: exodusII_io.C:2033
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:1562
std::unique_ptr< NumericVector< Number > > solution
Data structure to hold solution values.
Definition: system.h:1593
void init(triangulateio &t)
Initializes the fields of t to nullptr/0 as necessary.
static std::unique_ptr< FEGenericBase > build(const unsigned int dim, const FEType &type)
Builds a specific finite element type.
libmesh_assert(ctx)
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:1357
communicator & get()
virtual void close()=0
Calls the NumericVector&#39;s internal assembly routines, ensuring that the values are consistent across ...
This class extends EigenSystem to allow a simple way of solving (standard or generalized) eigenvalue ...
int main(int argc, char **argv)
PetscErrorCode form_matrixA(SNES snes, Vec x, Mat jac, Mat pc, void *ctx)
LibmeshPetscCallQ(DMShellGetContext(dm, &ctx))
virtual void update()
Update the local values to reflect the solution on neighboring processors.
Definition: system.C:510
virtual void close()=0
Calls the SparseMatrix&#39;s internal assembly routines, ensuring that the values are consistent across p...
const FEType & variable_type(const unsigned int i) const
Definition: system.h:2508
std::unique_ptr< NumericVector< Number > > create_wrapped_function(CondensedEigenSystem &sys, Vec f)
T & set(const std::string &)
Definition: parameters.h:469
OStreamProxy out
void resize(const unsigned int new_m, const unsigned int new_n)
Resizes the matrix to the specified size and calls zero().
Definition: dense_matrix.h:895
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:140
This class implements specific orders of Gauss quadrature.
unsigned int mesh_dimension() const
Definition: mesh_base.C:372
Parameters parameters
Data structure holding arbitrary parameters.
This class provides a nice interface to the PETSc C-based AIJ data structures for parallel...
Definition: petsc_matrix.h:61
virtual void reinit() override
Reinitializes the member data fields associated with the system, so that, e.g., assemble() may be use...
void * ctx
virtual void init()
Initialize all the systems.
PetscFunctionReturn(LIBMESH_PETSC_SUCCESS)
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.
The Mesh class is a thin wrapper, around the ReplicatedMesh class by default.
Definition: mesh.h:50
PetscErrorCode form_functionA(SNES snes, Vec x, Vec Ax, void *ctx)
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:1010
const DofMap & get_dof_map() const
Definition: system.h:2374
dof_id_type n_constrained_dofs() const
Definition: system.C:128
void update_current_local_solution(CondensedEigenSystem &sys, Vec x)