LehrFEM++ 1.0.0
A simple Finite Element Library for teaching
offset_function.cc
1#include "offset_function.h"
2
3#include <lf/assemble/assemble.h>
4#include <lf/assemble/coomatrix.h>
5#include <lf/base/base.h>
6#include <lf/mesh/utils/codim_mesh_data_set.h>
7#include <lf/mesh/utils/utils.h>
8#include <lf/uscalfe/lagr_fe.h>
9
10#include <algorithm>
11#include <iostream>
12#include <vector>
13
14#include "utils.h"
15
17
19 std::shared_ptr<const lf::mesh::Mesh> mesh,
21 : mesh_(std::move(mesh)), boundary_(boundary) {}
22
24 const lf::mesh::Entity &entity) const {
25 // Use the vertex coordinates to compute the local normals on the edges
26 const Eigen::Matrix<double, 2, 3> normals =
28 // Construct the basis functions from the curl of the standard hat functions
29 const auto *geom = entity.Geometry();
31 const Eigen::MatrixXd ref_grads =
32 hat_func.GradientsReferenceShapeFunctions(Eigen::VectorXd::Zero(2))
33 .transpose();
34 const Eigen::MatrixXd J_inv_trans =
35 geom->JacobianInverseGramian(Eigen::VectorXd::Zero(2));
36 const Eigen::MatrixXd grads = J_inv_trans * ref_grads;
37 Eigen::MatrixXd basis_funct(2, 3);
38 basis_funct << grads.row(1), -grads.row(0);
39 const auto nodes = entity.SubEntities(2);
40 const auto edges = entity.SubEntities(1);
41 // Count the number of boundary nodes and edges belonging to this triangle
42 // to get the size of the element matrix
43 int boundary_node_count = 0;
44 int boundary_edge_count = 0;
45 for (const auto *const node : nodes) {
46 if (boundary_(*node)) {
47 ++boundary_node_count;
48 }
49 }
50 for (const auto *const edge : edges) {
51 if (boundary_(*edge)) {
52 ++boundary_edge_count;
53 }
54 }
55 // Assemble the element matrix itself
56 Eigen::MatrixXd elem_mat = Eigen::MatrixXd::Zero(
57 std::max(1, boundary_edge_count), std::max(1, boundary_node_count));
58 int col_idx = 0;
59 for (int node_idx = 0; node_idx < 3; ++node_idx) {
60 int row_idx = 0;
61 const auto &node = *nodes[node_idx];
62 if (boundary_(node)) {
63 for (int edge_idx = 0; edge_idx < 3; ++edge_idx) {
64 const auto &edge = *edges[edge_idx];
65 if (boundary_(edge)) {
66 elem_mat(row_idx, col_idx) =
67 normals.col(edge_idx).transpose() * basis_funct.col(node_idx);
68 ++row_idx;
69 }
70 }
71 ++col_idx;
72 }
73 }
74 return elem_mat;
75}
76
77Eigen::VectorXd createOffsetFunction(
78 const std::shared_ptr<const lf::mesh::Mesh> &mesh,
80 const lf::assemble::DofHandler &dofh,
81 const std::function<Eigen::Vector2d(const lf::mesh::Entity &)>
82 &dirichlet_data,
83 const Eigen::SparseMatrix<double> &A) {
84 // Create a new dofhandler for the boundary nodes of the mesh
85 lf::assemble::DynamicFEDofHandler boundary_node_dofh(
86 mesh, [&](const lf::mesh::Entity &entity) {
87 if (entity.RefEl() == lf::base::RefElType::kPoint && boundary(entity)) {
88 return 1;
89 }
90 return 0;
91 });
92 // Create a new dofhandler for the boundary edges of the mesh
93 lf::assemble::DynamicFEDofHandler boundary_edge_dofh(
94 mesh, [&](const lf::mesh::Entity &entity) {
95 if (entity.RefEl() == lf::base::RefElType::kSegment &&
96 boundary(entity)) {
97 return 1;
98 }
99 return 0;
100 });
101 // Assemble a matrix mapping the basis function coefficients on the boundary
102 // nodes to the normal jumps over the boundary edges
103 lf::assemble::COOMatrix<double> J(boundary_edge_dofh.NumDofs() + 1,
104 boundary_node_dofh.NumDofs() + 1);
105 PiecewiseBoundaryNormalJumpAssembler element_matrix_provider(mesh, boundary);
106 lf::assemble::AssembleMatrixLocally(0, boundary_node_dofh, boundary_edge_dofh,
107 element_matrix_provider, J);
108 // Add the lagrange multiplier needed such that the average over all
109 // coefficients is zero
110 for (lf::base::size_type i = 0; i < boundary_node_dofh.NumDofs(); ++i) {
111 J.AddToEntry(boundary_edge_dofh.NumDofs(), i, 1);
112 }
113 for (lf::base::size_type i = 0; i < boundary_edge_dofh.NumDofs(); ++i) {
114 J.AddToEntry(i, boundary_node_dofh.NumDofs(), 1);
115 }
116 Eigen::SparseMatrix<double> Js = J.makeSparse();
117 // Assemble the right hand side from the provided dirichlet data
118 Eigen::VectorXd rhs = Eigen::VectorXd::Zero(boundary_edge_dofh.NumDofs() + 1);
119 for (const auto *const cell : mesh->Entities(0)) {
120 const Eigen::Matrix<double, 2, 3> normals =
122 const auto edges = cell->SubEntities(1);
123 for (int edge_idx = 0; edge_idx < 3; ++edge_idx) {
124 const auto &edge = *edges[edge_idx];
125 if (boundary(edge)) {
126 rhs[boundary_edge_dofh.GlobalDofIndices(edge)[0]] =
127 normals.col(edge_idx).transpose() * dirichlet_data(edge);
128 }
129 }
130 }
131 // Solve for the basis function coefficients of the offset function ordered
132 // with the boundary dof handler
133 Eigen::SparseLU<Eigen::SparseMatrix<double>> solver;
134 solver.compute(Js);
135 const Eigen::VectorXd offset_function_local =
136 solver.solve(rhs).head(boundary_node_dofh.NumDofs());
137 // Compute the full offset function including the jump terms
138 Eigen::VectorXd offset_function = Eigen::VectorXd::Zero(dofh.NumDofs());
139 for (lf::assemble::size_type node_idx = 0;
140 node_idx < boundary_node_dofh.NumDofs(); ++node_idx) {
141 const auto &node = boundary_node_dofh.Entity(node_idx);
142 offset_function[dofh.GlobalDofIndices(node)[0]] =
143 offset_function_local[node_idx];
144 }
145 offset_function.tail(mesh->NumEntities(1)) =
146 A.block(mesh->NumEntities(2), 0, mesh->NumEntities(1), A.cols()) *
147 offset_function;
148 return offset_function;
149}
150
151} // end namespace projects::ipdg_stokes::assemble
A temporary data structure for matrix in COO format.
Definition: coomatrix.h:52
A general (interface) class for DOF handling, see Lecture Document Paragraph 2.7.4....
Definition: dofhandler.h:109
virtual nonstd::span< const gdof_idx_t > GlobalDofIndices(const lf::mesh::Entity &entity) const =0
access to indices of global dof's belonging to an entity
virtual size_type NumDofs() const =0
total number of dof's handled by the object
Dof handler allowing variable local dof layouts.
Definition: dofhandler.h:472
Interface class representing a topological entity in a cellular complex
Definition: entity.h:39
virtual const geometry::Geometry * Geometry() const =0
Describes the geometry of this entity.
virtual nonstd::span< const Entity *const > SubEntities(unsigned rel_codim) const =0
Return all sub entities of this entity that have the given codimension (w.r.t. this entity!...
virtual base::RefEl RefEl() const =0
Describes the reference element type of this entity.
Linear Lagrange finite element on triangular reference element.
Definition: lagr_fe.h:58
Eigen::Matrix< SCALAR, Eigen::Dynamic, Eigen::Dynamic > GradientsReferenceShapeFunctions(const Eigen::MatrixXd &refcoords) const override
Computation of the gradients of all reference shape functions in a number of points.
Definition: lagr_fe.h:117
Local assembler for a matrix mapping boundary basis function coefficients to jumps.
Eigen::MatrixXd Eval(const lf::mesh::Entity &entity) const
Compute the element matrix for some triangle of the mesh.
PiecewiseBoundaryNormalJumpAssembler(std::shared_ptr< const lf::mesh::Mesh > mesh, const lf::mesh::utils::MeshDataSet< bool > &boundary)
Constructor.
void AssembleMatrixLocally(dim_t codim, const DofHandler &dof_handler_trial, const DofHandler &dof_handler_test, ENTITY_MATRIX_PROVIDER &entity_matrix_provider, TMPMATRIX &matrix)
Assembly function for standard assembly of finite element matrices.
Definition: assembler.h:113
unsigned int size_type
general type for variables related to size of arrays
Definition: base.h:24
lf::base::size_type size_type
@ kSegment
Returns the (1-dimensional) reference segment.
@ kPoint
Returns the (0-dimensional) reference point.
Eigen::VectorXd createOffsetFunction(const std::shared_ptr< const lf::mesh::Mesh > &mesh, const lf::mesh::utils::MeshDataSet< bool > &boundary, const lf::assemble::DofHandler &dofh, const std::function< Eigen::Vector2d(const lf::mesh::Entity &)> &dirichlet_data, const Eigen::SparseMatrix< double > &A)
Compute the offset function for given boundary conditions.
Eigen::Matrix< double, 2, 3 > computeOutwardNormals(const lf::mesh::Entity &entity)
Compute the outward pointing normals of a triangle.
Definition: utils.cc:7