Edit on GitHub
- Attention
- The contents of this page are discussed in Lecture Document Subsection 2.7.6. Please read this section before using the quick reference.
Overview
LehrFEM++ provides a number of convenient functions for working with essential boundary conditions.
To fix degrees of freedom (DoFs) on the boundary, we can use the functions lf::assemble::FixFlaggedSolutionComponents
or lf::assemble::FixFlaggedSolutionCompAlt
(see also Quick Reference - Assembly). Both functions take a function as an argument that returns a pair of a boolean and a double: std::pair<bool, double> selector(unsigned int dof_idx)
. The selector function returns whether the DoF is to be fixed and, if so, the value it should be fixed to.
We can obtain the boundary flags as a MeshDataSet using:
The second argument is the co-dimension of the entities we are interested in. To fix all DoFs on the boundary (including those associated with edges and cells), omit the second argument.
One function on the whole boundary
Let \(g\) be a function defining the values on the boundary. To get the function values of \(g\) at the boundary nodes, wrap it in a MeshFunction:
std::vector<std::pair<bool, double>> boundary_val =
This returns a std::vector<std::pair<bool, scalar_t>>
. To create our selector:
auto selector = [&](unsigned int dof_idx) -> std::pair<bool, double> {
return boundary_val[dof_idx];
};
If \(g\) has different definitions on different parts of the boundary (e.g., \(g = 0\) on \(\gamma_0\) and \(g = 1\) on \(\gamma_1\)), try to express \(g\) as a lambda function with an if-else statement.
Constant value on the boundary
The selector becomes:
auto selector = [&](unsigned int dof_idx) -> std::pair<bool, double> {
if (bd_flags(dofh.Entity(dof_idx))) {
return std::make_pair(true, 1.0);
} else {
return std::make_pair(false, 0.0);
}
};
BC only on part of the boundary
We need to create our own bd_flags
. Initialize a MeshDataSet
with default value false
:
Then loop over nodes and edges separately and set bd_flags
to true for the nodes/edges where the boundary condition should be applied.
for (const auto& edge : fe_space->Mesh()->Entities(1)) {
if (condition) bd_flags_part(*edge) = true;
}
for (const auto& node : fe_space->Mesh()->Entities(2)) {
}
Example (Essential boundary conditions)
auto mesh_factory = std::make_unique<mesh::hybrid2d::MeshFactory>(2);
auto gmsh_reader = io::GmshReader(std::move(mesh_factory), "mesh.msh");
auto mesh = gmsh_reader.mesh();
auto fe_space =
std::make_shared<lf::uscalfe::FeSpaceLagrangeO1<double>>(mesh);
auto mf_boundary =
mesh::utils::MeshFunctionGlobal([&](const Eigen::Vector2d& x) {
return std::cos(x[0]) * std::sin(x[1]);
});
*fe_space, base::PredicateTrue{}, mf_boundary);
assemble::COOMatrix<double> lhs(fe_space->LocGlobMap().NumDofs(),
fe_space->LocGlobMap().NumDofs());
auto mf_one = mesh::utils::MeshFunctionConstant(1.);
auto mf_zero = mesh::utils::MeshFunctionConstant(0.);
fe_space, mf_one, mf_zero);
fe_space->LocGlobMap(), matrix_provider, lhs);
Eigen::VectorXd rhs(fe_space->LocGlobMap().NumDofs());
[&](unsigned int idx) { return boundary_cond[idx]; }, lhs, rhs);
auto lhs_sparse = lhs.makeSparse();
Eigen::SimplicialLDLT<decltype(lhs_sparse)> solver;
solver.compute(lhs_sparse);
auto x = solver.solve(rhs);