libMesh
Typedefs | Functions
vector_fe_ex7.C File Reference

Go to the source code of this file.

Typedefs

typedef MatrixXcd EigenMatrix
 
typedef VectorXcd EigenVector
 

Functions

void fe_assembly (EquationSystems &es, bool global_solve)
 
void assemble_divgrad (EquationSystems &es, const std::string &system_name)
 
int main (int argc, char **argv)
 
void assemble_divgrad (EquationSystems &es, const std::string &libmesh_dbg_var(system_name))
 
int main ()
 

Typedef Documentation

◆ EigenMatrix

typedef MatrixXd EigenMatrix

Definition at line 149 of file vector_fe_ex7.C.

◆ EigenVector

typedef VectorXd EigenVector

Definition at line 150 of file vector_fe_ex7.C.

Function Documentation

◆ assemble_divgrad() [1/2]

void assemble_divgrad ( EquationSystems es,
const std::string &  system_name 
)

Referenced by main().

◆ assemble_divgrad() [2/2]

void assemble_divgrad ( EquationSystems es,
const std::string &  libmesh_dbg_varsystem_name 
)

Definition at line 726 of file vector_fe_ex7.C.

References fe_assembly().

727 {
728  libmesh_assert_equal_to(system_name, "Lambda");
729  fe_assembly(es, /*global_solve=*/true);
730 }
void fe_assembly(EquationSystems &es, bool global_solve)

◆ fe_assembly()

void fe_assembly ( EquationSystems es,
bool  global_solve 
)

The scalar solution at the quadrature points

Data structures for computing the enriched scalar solution

Definition at line 326 of file vector_fe_ex7.C.

References libMesh::SparseMatrix< T >::add_matrix(), libMesh::System::add_vector(), libMesh::FEGenericBase< OutputType >::build(), libMesh::System::current_local_solution, dim, libMesh::FIFTH, DivGradExactSolution::forcing(), libMesh::Parameters::get(), libMesh::System::get_dof_map(), libMesh::EquationSystems::get_mesh(), libMesh::EquationSystems::get_system(), libMesh::index_range(), libMesh::libmesh_assert(), libMesh::DenseMatrix< T >::lu_solve(), libMesh::make_range(), mesh, libMesh::MeshBase::mesh_dimension(), libMesh::QBase::n_points(), libMesh::EquationSystems::parameters, libMesh::Real, libMesh::LinearImplicitSystem::reinit(), libMesh::DenseVector< T >::resize(), libMesh::DenseMatrix< T >::resize(), DivGradExactSolution::scalar(), libMesh::DenseMatrix< T >::svd_solve(), libMesh::System::variable_number(), and libMesh::System::variable_type().

Referenced by assemble_divgrad(), and main().

327 {
328  const MeshBase & mesh = es.get_mesh();
329  const unsigned int dim = mesh.mesh_dimension();
330  // Are our elements simplicial?
331  const bool simplicial = es.parameters.get<bool>("simplicial");
332 
333  // The div-grad, e.g. vector-scalar system
334  auto & system = es.get_system<System>("DivGrad");
335  // Our implicit Lagrange multiplier system
336  auto & lambda_system = es.get_system<LinearImplicitSystem>("Lambda");
337 
338  const auto & dof_map = system.get_dof_map();
339  const auto & lambda_dof_map = lambda_system.get_dof_map();
340 
341  const FEType vector_fe_type = dof_map.variable_type(system.variable_number("u"));
342  const FEType scalar_fe_type = dof_map.variable_type(system.variable_number("p"));
343  const FEType enriched_scalar_fe_type =
344  dof_map.variable_type(system.variable_number("p_enriched"));
345  const FEType lambda_fe_type =
346  lambda_dof_map.variable_type(lambda_system.variable_number("lambda"));
347 
348  // Volumetric FE objects
349  std::unique_ptr<FEVectorBase> vector_fe(FEVectorBase::build(dim, vector_fe_type));
350  std::unique_ptr<FEBase> scalar_fe(FEBase::build(dim, scalar_fe_type));
351  std::unique_ptr<FEBase> enriched_scalar_fe(FEBase::build(dim, enriched_scalar_fe_type));
352 
353  // Volumetric quadrature rule
354  QGauss qrule(dim, FIFTH);
355 
356  // Attach quadrature rules for the FE objects that we will reinit within the element "volume"
357  vector_fe->attach_quadrature_rule(&qrule);
358  scalar_fe->attach_quadrature_rule(&qrule);
359  enriched_scalar_fe->attach_quadrature_rule(&qrule);
360 
361  // Declare finite element objects for boundary integration
362  std::unique_ptr<FEVectorBase> vector_fe_face(FEVectorBase::build(dim, vector_fe_type));
363  std::unique_ptr<FEBase> enriched_scalar_fe_face(FEBase::build(dim, enriched_scalar_fe_type));
364  std::unique_ptr<FEBase> lambda_fe_face(FEBase::build(dim, lambda_fe_type));
365 
366  // Boundary integration requires one quadrature rule with dimensionality one
367  // less than the dimensionality of the element.
368  QGauss qface(dim - 1, FIFTH);
369 
370  // Attach quadrature rules for the FE objects that we will reinit on the element faces
371  vector_fe_face->attach_quadrature_rule(&qface);
372  enriched_scalar_fe_face->attach_quadrature_rule(&qface);
373  lambda_fe_face->attach_quadrature_rule(&qface);
374 
375  // pre-request our required volumetric data
376  const auto & JxW = vector_fe->get_JxW();
377  const auto & q_point = vector_fe->get_xyz();
378  const auto & vector_phi = vector_fe->get_phi();
379  const auto & scalar_phi = scalar_fe->get_phi();
380  const auto & enriched_scalar_phi = enriched_scalar_fe->get_phi();
381  const auto & div_vector_phi = vector_fe->get_div_phi();
382 
383  // pre-request our required element face data
384  const auto & vector_phi_face = vector_fe_face->get_phi();
385  const auto & enriched_scalar_phi_face = enriched_scalar_fe_face->get_phi();
386  const auto & lambda_phi_face = lambda_fe_face->get_phi();
387  const auto & JxW_face = vector_fe_face->get_JxW();
388  const auto & qface_point = vector_fe_face->get_xyz();
389  const auto & normals = vector_fe_face->get_normals();
390 
391  //
392  // We follow the notation of Cockburn in "A Charactrization of Hybridized
393  // Mixed methods for Second Order Elliptic problems"for the element
394  // matrices/vectors. We will need "Eigen" versions of many of the
395  // matrices/vectors because the libMesh DenseMatrix doesn't have an inverse
396  // API
397  //
398 
399  // LM matrix and RHS after eliminating vector and scalar dofs
400  DenseMatrix<Number> E_libmesh;
401  DenseVector<Number> H_libmesh;
402  EigenMatrix E;
403  EigenMatrix H;
404  // Auxiliary matrices and RHS. A is vector-vector. B is vector-scalar. C is
405  // vector-LM. Sinv is the inverse of the Schur complement S which is given by
406  // S = Bt * A^{-1} * B. G is the RHS of the vector equation (e.g. the
407  // Dirichlet condition). F is the RHS of the scalar equation (resulting from
408  // the body force or MMS force in this case). Hg is the post-elimination
409  // version of G, and Hf is the post-elimination version of F
410  EigenMatrix A, Ainv, B, Bt, C, Ct, Sinv;
411  EigenVector G, F, Hg, Hf;
412  // element matrix for boundary LM dofs. We have to have this because our
413  // SIDE_HIERARCHIC variables live on *all* element faces, not just interior
414  // ones.
415  EigenMatrix L;
416 
417  // Lambda eigen vector for constructing vector and scalar solutions
418  EigenVector Lambda;
419  // The lambda solution at the quadrature points
420  std::vector<Number> lambda_qps;
422  std::vector<Number> scalar_qps;
423 
425  DenseMatrix<Number> K_enriched_scalar;
426  DenseVector<Number> F_enriched_scalar, U_enriched_scalar;
427 
428  // Containers for dof indices
429  std::vector<dof_id_type> vector_dof_indices;
430  std::vector<dof_id_type> scalar_dof_indices;
431  std::vector<dof_id_type> enriched_scalar_dof_indices;
432  std::vector<dof_id_type> lambda_dof_indices;
433  std::vector<Number> lambda_solution_std_vec;
434 
435  // The global system matrix
436  SparseMatrix<Number> & matrix = lambda_system.get_system_matrix();
437 
438  for (const auto & elem : mesh.active_local_element_ptr_range())
439  {
440  // Retrive our dof indices for all fields
441  dof_map.dof_indices(elem, vector_dof_indices, system.variable_number("u"));
442  dof_map.dof_indices(elem, scalar_dof_indices, system.variable_number("p"));
443  lambda_dof_map.dof_indices(elem, lambda_dof_indices, lambda_system.variable_number("lambda"));
444 
445  const auto vector_n_dofs = vector_dof_indices.size();
446  const auto scalar_n_dofs = scalar_dof_indices.size();
447  const auto lambda_n_dofs = lambda_dof_indices.size();
448 
449  // Reinit our volume FE objects
450  vector_fe->reinit(elem);
451  scalar_fe->reinit(elem);
452 
453  libmesh_assert_equal_to(vector_n_dofs, vector_phi.size());
454  libmesh_assert_equal_to(scalar_n_dofs, scalar_phi.size());
455 
456  // prepare our matrix/vector data structures for the vector equation
457  A.setZero(vector_n_dofs, vector_n_dofs);
458  B.setZero(vector_n_dofs, scalar_n_dofs);
459  C.setZero(vector_n_dofs, lambda_n_dofs);
460  G.setZero(vector_n_dofs);
461  // and for the scalar equation
462  Bt.setZero(scalar_n_dofs, vector_n_dofs);
463  F.setZero(scalar_n_dofs);
464  // and for the LM equation
465  Ct.setZero(lambda_n_dofs, vector_n_dofs);
466  L.setZero(lambda_n_dofs, lambda_n_dofs);
467 
468  for (const auto qp : make_range(qrule.n_points()))
469  {
470  // Vector equation dependence on vector dofs
471  for (const auto i : make_range(vector_n_dofs))
472  for (const auto j : make_range(vector_n_dofs))
473  A(i, j) += JxW[qp] * (vector_phi[i][qp] * vector_phi[j][qp]);
474 
475  // Vector equation dependence on scalar dofs
476  for (const auto i : make_range(vector_n_dofs))
477  for (const auto j : make_range(scalar_n_dofs))
478  B(i, j) -= JxW[qp] * (div_vector_phi[i][qp] * scalar_phi[j][qp]);
479 
480  // Scalar equation dependence on vector dofs
481  for (const auto i : make_range(scalar_n_dofs))
482  for (const auto j : make_range(vector_n_dofs))
483  Bt(i, j) -= JxW[qp] * (scalar_phi[i][qp] * div_vector_phi[j][qp]);
484 
485  // This is the end of the matrix summation loop
486  // Now we build the element right-hand-side contribution.
487  // This involves a single loop in which we integrate the "forcing
488  // function" in the PDE against the scalar test functions (k).
489  {
490  const Real x = q_point[qp](0);
491  const Real y = q_point[qp](1);
492  const Real z = q_point[qp](2);
493 
494  // "f" is the forcing function for the Poisson equation, which is
495  // just the divergence of the exact solution for the vector field.
496  // This is the well-known "method of manufactured solutions".
497  Real f = 0;
498  if (dim == 2)
499  f = DivGradExactSolution().forcing(x, y);
500  else if (dim == 3)
501  f = DivGradExactSolution().forcing(x, y, z);
502 
503  // Scalar equation RHS
504  for (const auto i : make_range(scalar_n_dofs))
505  F(i) -= JxW[qp] * scalar_phi[i][qp] * f;
506  }
507  }
508 
509  {
510  for (auto side : elem->side_index_range())
511  {
512  // Reinit our face FE objects
513  vector_fe_face->reinit(elem, side);
514  libmesh_assert_equal_to(vector_n_dofs, vector_phi_face.size());
515  lambda_fe_face->reinit(elem, side);
516 
517  if (elem->neighbor_ptr(side) == nullptr)
518  for (const auto qp : make_range(qface.n_points()))
519  {
520  const Real xf = qface_point[qp](0);
521  const Real yf = qface_point[qp](1);
522  const Real zf = qface_point[qp](2);
523 
524  // The boundary value for scalar field.
525  Real scalar_value = 0;
526  if (dim == 2)
527  scalar_value = DivGradExactSolution().scalar(xf, yf);
528  else if (dim == 3)
529  scalar_value = DivGradExactSolution().scalar(xf, yf, zf);
530 
531  // External boundary -> Dirichlet faces -> Vector equaton RHS
532  for (const auto i : make_range(vector_n_dofs))
533  G(i) -= JxW_face[qp] * (vector_phi_face[i][qp] * normals[qp]) * scalar_value;
534 
535  // Need to do something with the external boundary LM dofs to prevent the matrix from
536  // being singular
537  for (const auto i : make_range(lambda_n_dofs))
538  for (const auto j : make_range(lambda_n_dofs))
539  L(i, j) += JxW_face[qp] * lambda_phi_face[i][qp] * lambda_phi_face[j][qp];
540  }
541  else
542  for (const auto qp : make_range(qface.n_points()))
543  {
544  // Vector equation dependence on LM dofs
545  for (const auto i : make_range(vector_n_dofs))
546  for (const auto j : make_range(lambda_n_dofs))
547  C(i, j) +=
548  JxW_face[qp] * (vector_phi_face[i][qp] * normals[qp]) * lambda_phi_face[j][qp];
549 
550  // LM equation dependence on vector dofs
551  for (const auto i : make_range(lambda_n_dofs))
552  for (const auto j : make_range(vector_n_dofs))
553  Ct(i, j) +=
554  JxW_face[qp] * lambda_phi_face[i][qp] * (vector_phi_face[j][qp] * normals[qp]);
555  }
556  }
557  }
558 
559  Ainv = A.inverse();
560  // Compute the Schur complement inverse
561  Sinv = (Bt * Ainv * B).inverse();
562  // These equations can be derived at by eliminating the u and p dofs from the system
563  // (A B C)(u) (G)
564  // (Bt 0 0)(p) = (F)
565  // (Ct 0 0)(lambda) (0)
566  E = Ct * Ainv * (A - B * Sinv * Bt) * Ainv * C;
567  E = E + L;
568  Hg = Ct * Ainv * (A - B * Sinv * Bt) * Ainv * G;
569  Hf = Ct * Ainv * B * Sinv * F;
570  H = Hg + Hf;
571 
572  // Build our libMesh data structures from the Eigen ones
573  E_libmesh.resize(E.rows(), E.cols());
574  H_libmesh.resize(H.size());
575  libmesh_assert((E.rows() == E.cols()) && (E.rows() == H.size()));
576  for (const auto i : make_range(E.rows()))
577  {
578  H_libmesh(i) = H(i);
579  for (const auto j : make_range(E.cols()))
580  E_libmesh(i, j) = E(i, j);
581  }
582 
583  if (global_solve)
584  {
585  // We were performing our finite element assembly for the implicit solve step of our
586  // example. Add our local element vectors/matrices into the global system
587  dof_map.constrain_element_matrix_and_vector(E_libmesh, H_libmesh, lambda_dof_indices);
588  matrix.add_matrix(E_libmesh, lambda_dof_indices);
589  lambda_system.rhs->add_vector(H_libmesh, lambda_dof_indices);
590  }
591  else
592  {
593  //
594  // We are doing our finite element assembly for the second time. We now know the Lagrange
595  // multiplier solution. With that and the local element matrices and vectors we can compute
596  // the vector and scalar solutions
597  //
598 
599  Lambda.resize(lambda_n_dofs);
600  lambda_system.current_local_solution->get(lambda_dof_indices, lambda_solution_std_vec);
601  for (const auto i : make_range(lambda_n_dofs))
602  Lambda(i) = lambda_solution_std_vec[i];
603  const auto scalar_soln = Sinv * Bt * Ainv * G - Sinv * F - Sinv * Bt * Ainv * C * Lambda;
604  const auto vector_soln = Ainv * (G - B * scalar_soln - C * Lambda);
605  for (const auto i : make_range(vector_n_dofs))
606  system.solution->set(vector_dof_indices[i], vector_soln(i));
607  for (const auto i : make_range(scalar_n_dofs))
608  system.solution->set(scalar_dof_indices[i], scalar_soln(i));
609 
610 #if !defined(LIBMESH_HAVE_PETSC) || !defined(LIBMESH_USE_REAL_NUMBERS)
611  if (!simplicial)
612  // We don't support SVD solves in this configuration
613  continue;
614 #endif
615 
616  //
617  // Now solve for the enriched scalar solution using our Lagrange multiplier solution and, for
618  // non-simplexes, the lower-order scalar solution. Note that the Lagrange multiplier
619  // represents the trace of p so it is a logical choice to leverage in this postprocessing
620  // stage!
621  //
622 
623  dof_map.dof_indices(elem, enriched_scalar_dof_indices, system.variable_number("p_enriched"));
624  const auto enriched_scalar_n_dofs = enriched_scalar_dof_indices.size();
625  const auto m = simplicial ? lambda_n_dofs : lambda_n_dofs + 1;
626  const auto n = enriched_scalar_n_dofs;
627 
628  K_enriched_scalar.resize(m, n);
629  F_enriched_scalar.resize(m);
630  U_enriched_scalar.resize(n);
631 
632  // L2 projection of the enriched scalar into the LM space
633  for (const auto side : elem->side_index_range())
634  {
635  vector_fe_face->reinit(elem, side); // for JxW_face and qface_point
636  enriched_scalar_fe_face->reinit(elem, side);
637  lambda_fe_face->reinit(elem, side);
638  if (elem->neighbor_ptr(side) == nullptr)
639  for (const auto qp : make_range(qface.n_points()))
640  {
641  const Real xf = qface_point[qp](0);
642  const Real yf = qface_point[qp](1);
643  const Real zf = qface_point[qp](2);
644 
645  // The boundary value for scalar field.
646  Real scalar_value = 0;
647  if (dim == 2)
648  scalar_value = DivGradExactSolution().scalar(xf, yf);
649  else if (dim == 3)
650  scalar_value = DivGradExactSolution().scalar(xf, yf, zf);
651 
652  for (const auto i : make_range(lambda_n_dofs))
653  {
654  F_enriched_scalar(i) += JxW_face[qp] * lambda_phi_face[i][qp] * scalar_value;
655  for (const auto j : make_range(enriched_scalar_n_dofs))
656  K_enriched_scalar(i, j) +=
657  JxW_face[qp] * lambda_phi_face[i][qp] * enriched_scalar_phi_face[j][qp];
658  }
659  }
660  else
661  {
662  // compute local face lambda solution
663  lambda_qps.resize(qface.n_points());
664  for (auto & lambda_qp : lambda_qps)
665  lambda_qp = 0;
666  for (const auto qp : make_range(qface.n_points()))
667  for (const auto i : index_range(lambda_solution_std_vec))
668  lambda_qps[qp] += lambda_solution_std_vec[i] * lambda_phi_face[i][qp];
669 
670  for (const auto qp : make_range(qface.n_points()))
671  for (const auto i : make_range(lambda_n_dofs))
672  {
673  F_enriched_scalar(i) += JxW_face[qp] * lambda_phi_face[i][qp] * lambda_qps[qp];
674  for (const auto j : make_range(enriched_scalar_n_dofs))
675  K_enriched_scalar(i, j) +=
676  JxW_face[qp] * lambda_phi_face[i][qp] * enriched_scalar_phi_face[j][qp];
677  }
678  }
679  }
680 
681  if (simplicial)
682  K_enriched_scalar.lu_solve(F_enriched_scalar, U_enriched_scalar);
683  else
684  {
685  //
686  // For tensor product elements, the system of equations coming from the L2 projection into
687  // the Lagrange multiplier space is singular. To make the system nonsingular, we add an L2
688  // projection into the low-order scalar solution space.
689  //
690 
691  enriched_scalar_fe->reinit(elem);
692  libmesh_assert(scalar_n_dofs == 1);
693  libmesh_assert(scalar_soln.size() == 1);
694  libmesh_assert(scalar_phi.size() == 1);
695 
696  scalar_qps.resize(qrule.n_points());
697  for (auto & scalar_qp : scalar_qps)
698  scalar_qp = 0;
699  for (const auto qp : make_range(qrule.n_points()))
700  scalar_qps[qp] += scalar_soln(0) * scalar_phi[0][qp];
701  for (const auto qp : make_range(qrule.n_points()))
702  {
703  F_enriched_scalar(n) += JxW[qp] * scalar_phi[0][qp] * scalar_qps[qp];
704  for (const auto j : make_range(n))
705  K_enriched_scalar(n, j) += JxW[qp] * scalar_phi[0][qp] * enriched_scalar_phi[j][qp];
706  }
707  // We have more rows than columns so an LU solve isn't an option
708  K_enriched_scalar.svd_solve(F_enriched_scalar, U_enriched_scalar);
709  }
710 
711  // Our solution for the local enriched scalar dofs is complete. Insert into the global vector
712  system.solution->insert(U_enriched_scalar, enriched_scalar_dof_indices);
713  }
714  }
715 
716  if (!global_solve)
717  {
718  system.solution->close();
719  // Scatter solution into the current_solution which is used in error computation
720  system.update();
721  }
722 }
class FEType hides (possibly multiple) FEFamily and approximation orders, thereby enabling specialize...
Definition: fe_type.h:196
unsigned int dim
Manages consistently variables, degrees of freedom, coefficient vectors, matrices and linear solvers ...
void resize(const unsigned int n)
Resize the vector.
Definition: dense_vector.h:396
MeshBase & mesh
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
virtual void reinit() override
Reinitializes the member data fields associated with the system, so that, e.g., assemble() may be use...
VectorXcd EigenVector
const T_sys & get_system(std::string_view name) const
This is the MeshBase class.
Definition: mesh_base.h:75
unsigned int variable_number(std::string_view var) const
Definition: system.C:1609
Definition: assembly.h:38
virtual void add_matrix(const DenseMatrix< T > &dm, const std::vector< numeric_index_type > &rows, const std::vector< numeric_index_type > &cols)=0
Add the full matrix dm to the SparseMatrix.
const T & get(std::string_view) const
Definition: parameters.h:426
Manages consistently variables, degrees of freedom, and coefficient vectors.
Definition: system.h:96
libmesh_assert(ctx)
MatrixXcd EigenMatrix
void lu_solve(const DenseVector< T > &b, DenseVector< T > &x)
Solve the system Ax=b given the input vector b.
const FEType & variable_type(const unsigned int i) const
Definition: system.h:2508
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
Real forcing(Real x, Real y)
const MeshBase & get_mesh() const
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.
std::unique_ptr< NumericVector< Number > > current_local_solution
All the values I need to compute my contribution to the simulation at hand.
Definition: system.h:1605
const DofMap & get_dof_map() const
Definition: system.h:2374
void svd_solve(const DenseVector< T > &rhs, DenseVector< T > &x, Real rcond=std::numeric_limits< Real >::epsilon()) const
Solve the system of equations for in the least-squares sense.
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition: int_range.h:117
Real scalar(Real x, Real y)

◆ main() [1/2]

int main ( int  argc,
char **  argv 
)

Definition at line 160 of file vector_fe_ex7.C.

References libMesh::EquationSystems::add_system(), libMesh::System::add_variable(), assemble_divgrad(), libMesh::System::attach_assemble_function(), libMesh::ExactSolution::attach_exact_derivs(), libMesh::ExactSolution::attach_exact_values(), libMesh::MeshTools::Generation::build_cube(), libMesh::MeshTools::Generation::build_square(), libMesh::ExactSolution::compute_error(), libMesh::CONSTANT, libMesh::default_solver_package(), libMesh::ExactSolution::error_norm(), libMesh::ExactSolution::extra_quadrature_order(), fe_assembly(), libMesh::FIRST, libMesh::ExactSolution::hdiv_error(), libMesh::HDIV_SEMINORM, libMesh::TriangleWrapper::init(), libMesh::EquationSystems::init(), libMesh::INVALID_SOLVER_PACKAGE, libMesh::ExactSolution::l2_error(), libMesh::L2_RAVIART_THOMAS, mesh, libMesh::MONOMIAL, libMesh::out, libMesh::EquationSystems::parameters, libMesh::MeshTools::Modification::permute_elements(), libMesh::Parameters::set(), libMesh::SIDE_HIERARCHIC, libMesh::System::solve(), and libMesh::ExodusII_IO::write_equation_systems().

161 {
162  // Initialize libMesh.
163  LibMeshInit init(argc, argv);
164 
165  // This example requires a linear solver package.
166  libmesh_example_requires(libMesh::default_solver_package() != INVALID_SOLVER_PACKAGE,
167  "--enable-petsc, --enable-trilinos, or --enable-eigen");
168 
169  // Parse the input file.
170  GetPot infile("vector_fe_ex7.in");
171 
172  // But allow the command line to override it.
173  infile.parse_command_line(argc, argv);
174 
175  // Read in parameters from the command line and the input file.
176  const unsigned int dimension = infile("dim", 2);
177  const unsigned int grid_size = infile("grid_size", 15);
178 
179  // Skip higher-dimensional examples on a lower-dimensional libMesh build.
180  libmesh_example_requires(dimension <= LIBMESH_DIM, dimension << "D support");
181 
182  // Create a mesh, with dimension to be overridden later, distributed
183  // across the default MPI communicator.
184  Mesh mesh(init.comm());
185 
186  // Use the MeshTools::Generation mesh generator to create a uniform
187  // grid on the cube [-1,1]^D. To accomodate Raviart-Thomas elements, we must
188  // use TRI6/7 or QUAD8/9 elements in 2d, or TET14 or HEX27 in 3d.
189  const std::string elem_str = infile("element_type", std::string("TRI6"));
190 
191  libmesh_error_msg_if((dimension == 2 && elem_str != "TRI6" && elem_str != "TRI7" &&
192  elem_str != "QUAD8" && elem_str != "QUAD9") ||
193  (dimension == 3 && elem_str != "TET14" && elem_str != "HEX27"),
194  "You selected "
195  << elem_str
196  << " but this example must be run with TRI6, TRI7, QUAD8, or QUAD9 in 2d"
197  << " or with TET14, or HEX27 in 3d.");
198 
199  if (dimension == 2)
201  mesh, grid_size, grid_size, -1., 1., -1., 1., Utility::string_to_enum<ElemType>(elem_str));
202  else if (dimension == 3)
204  grid_size,
205  grid_size,
206  grid_size,
207  -1.,
208  1.,
209  -1.,
210  1.,
211  -1.,
212  1.,
213  Utility::string_to_enum<ElemType>(elem_str));
214 
215  // Make sure the code is robust against nodal reorderings.
217 
218  // Create an equation systems object.
219  EquationSystems equation_systems(mesh);
220  const bool simplicial = (elem_str == "TRI6") || (elem_str == "TRI7") || (elem_str == "TET14");
221  equation_systems.parameters.set<bool>("simplicial") = simplicial;
222 
223 
224  // Declare the system "DivGrad" and its variables.
225  auto & system = equation_systems.add_system<System>("DivGrad");
226 
227  // Add the LM system
228  auto & lm_system = equation_systems.add_system<LinearImplicitSystem>("Lambda");
229 
230  // Adds the variable "u" and "p" to "DivGrad". "u" will be our vector field
231  // whereas "p" will be the scalar field.
232  system.add_variable("u", FIRST, L2_RAVIART_THOMAS);
233  system.add_variable("p", CONSTANT, MONOMIAL);
234  // We also add a higher order version of our 'p' variable whose solution we
235  // will compute using the Lagrange multiplier field and, for non-simplexes,
236  // the low order 'p' solution
237  system.add_variable("p_enriched", FIRST, MONOMIAL);
238 
239  // Add our Lagrange multiplier to the implicit system
240  lm_system.add_variable("lambda", CONSTANT, SIDE_HIERARCHIC);
241 
242  // Give the system a pointer to the matrix assembly
243  // function. This will be called when needed by the library.
245 
246  // Initialize the data structures for the equation system.
247  equation_systems.init();
248 
249  // Solve the implicit system for the Lagrange multiplier
250  lm_system.solve();
251 
252  // Armed with our Lagrange multiplier solution, we can now compute the vector and scalar solutions
253  fe_assembly(equation_systems, /*global_solve=*/false);
254 
255  //
256  // Now we will compute our solution approximation errors
257  //
258 
259  ExactSolution exact_sol(equation_systems);
260 
261  if (dimension == 2)
262  {
263  SolutionFunction<2> soln_func;
264  SolutionGradient<2> soln_grad;
265 
266  // Build FunctionBase* containers to attach to the ExactSolution object.
267  std::vector<FunctionBase<Number> *> sols(1, &soln_func);
268  std::vector<FunctionBase<Gradient> *> grads(1, &soln_grad);
269 
270  exact_sol.attach_exact_values(sols);
271  exact_sol.attach_exact_derivs(grads);
272  }
273  else if (dimension == 3)
274  {
275  SolutionFunction<3> soln_func;
276  SolutionGradient<3> soln_grad;
277 
278  // Build FunctionBase* containers to attach to the ExactSolution object.
279  std::vector<FunctionBase<Number> *> sols(1, &soln_func);
280  std::vector<FunctionBase<Gradient> *> grads(1, &soln_grad);
281 
282  exact_sol.attach_exact_values(sols);
283  exact_sol.attach_exact_derivs(grads);
284  }
285 
286  // Use higher quadrature order for more accurate error results.
287  int extra_error_quadrature = infile("extra_error_quadrature", 2);
288  if (extra_error_quadrature)
289  exact_sol.extra_quadrature_order(extra_error_quadrature);
290 
291  // Compute the error.
292  exact_sol.compute_error("DivGrad", "u");
293  exact_sol.compute_error("DivGrad", "p");
294 #if !defined(LIBMESH_HAVE_PETSC) || !defined(LIBMESH_USE_REAL_NUMBERS)
295  if (simplicial)
296 #endif
297  exact_sol.compute_error("DivGrad", "p_enriched");
298 
299  // Print out the error values.
300  libMesh::out << "L2 error is: " << exact_sol.l2_error("DivGrad", "u") << std::endl;
301  libMesh::out << "HDiv semi-norm error is: " << exact_sol.error_norm("DivGrad", "u", HDIV_SEMINORM)
302  << std::endl;
303  libMesh::out << "HDiv error is: " << exact_sol.hdiv_error("DivGrad", "u") << std::endl;
304  libMesh::out << "L2 error for p is: " << exact_sol.l2_error("DivGrad", "p") << std::endl;
305 #if !defined(LIBMESH_HAVE_PETSC) || !defined(LIBMESH_USE_REAL_NUMBERS)
306  if (simplicial)
307 #endif
308  libMesh::out << "L2 error p_enriched is: " << exact_sol.l2_error("DivGrad", "p_enriched")
309  << std::endl;
310 
311 #ifdef LIBMESH_HAVE_EXODUS_API
312 
313  // We write the file in the ExodusII format.
314  ExodusII_IO(mesh).write_equation_systems("out.e", equation_systems);
315 
316 #endif // #ifdef LIBMESH_HAVE_EXODUS_API
317 
318  // All done.
319  return 0;
320 }
This class handles the computation of the L2 and/or H1 error for the Systems in the EquationSystems o...
This is the EquationSystems class.
The ExodusII_IO class implements reading meshes in the ExodusII file format from Sandia National Labs...
Definition: exodusII_io.h:52
Manages consistently variables, degrees of freedom, coefficient vectors, matrices and linear solvers ...
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 LibMeshInit class, when constructed, initializes the dependent libraries (e.g.
Definition: libmesh.h:90
void permute_elements(MeshBase &mesh)
Randomly permute the nodal ordering of each element (without twisting the element mapping)...
SolverPackage default_solver_package()
Definition: libmesh.C:1117
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
Manages consistently variables, degrees of freedom, and coefficient vectors.
Definition: system.h:96
void init(triangulateio &t)
Initializes the fields of t to nullptr/0 as necessary.
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
void fe_assembly(EquationSystems &es, bool global_solve)
void attach_assemble_function(void fptr(EquationSystems &es, const std::string &name))
Register a user function to use in assembling the system matrix and RHS.
Definition: system.C:2161
virtual void solve()
Solves the system.
Definition: system.h:347
OStreamProxy out
void assemble_divgrad(EquationSystems &es, const std::string &system_name)
The Mesh class is a thin wrapper, around the ReplicatedMesh class by default.
Definition: mesh.h:50
void build_cube(UnstructuredMesh &mesh, const unsigned int nx=0, const unsigned int ny=0, const unsigned int nz=0, const Real xmin=0., const Real xmax=1., const Real ymin=0., const Real ymax=1., const Real zmin=0., const Real zmax=1., const ElemType type=INVALID_ELEM, const bool gauss_lobatto_grid=false)
Builds a (elements) cube.

◆ main() [2/2]

int main ( )

Definition at line 735 of file vector_fe_ex7.C.

736 {
737  return 0;
738 }