6#define _USE_MATH_DEFINES
7#include <annulus_triag_mesh_builder.h>
8#include <build_system_matrix.h>
9#include <lf/assemble/dofhandler.h>
10#include <lf/io/gmsh_reader.h>
11#include <lf/io/vtk_writer.h>
12#include <lf/mesh/entity.h>
13#include <lf/mesh/hybrid2d/mesh_factory.h>
14#include <lf/mesh/utils/utils.h>
15#include <lf/quad/quad.h>
16#include <mesh_function_velocity.h>
18#include <piecewise_const_element_matrix_provider.h>
19#include <piecewise_const_element_vector_provider.h>
20#include <solution_to_mesh_data_set.h>
23#include <boost/program_options.hpp>
30using lf::uscalfe::operator-;
44Eigen::VectorXd solveNestedCylindersZeroBC(
45 const std::shared_ptr<const lf::mesh::Mesh> &mesh,
47 double omega2,
bool modified_penalty) {
49 auto f = [](
const Eigen::Vector2d & ) {
50 return Eigen::Vector2d::Zero();
53 const double eps = 1e-10;
54 auto dirichlet_funct = [&](
const lf::mesh::Entity &edge) -> Eigen::Vector2d {
55 const auto *
const geom = edge.Geometry();
56 const auto vertices = geom->Global(edge.RefEl().NodeCoords());
57 if (vertices.col(0).norm() <= R + eps &&
58 vertices.col(0).norm() >= R - eps &&
59 vertices.col(1).norm() <= R + eps &&
60 vertices.col(1).norm() >= R - eps) {
61 return omega2 * R * (vertices.col(1) - vertices.col(0)).normalized();
63 if (vertices.col(0).norm() <= r + eps &&
64 vertices.col(0).norm() >= r - eps &&
65 vertices.col(1).norm() <= r + eps &&
66 vertices.col(1).norm() >= r - eps) {
67 return omega1 * r * (vertices.col(0) - vertices.col(1)).normalized();
69 return Eigen::Vector2d::Zero();
73 for (
const auto *ep : mesh->Entities(1)) {
74 dirichlet(*ep) = dirichlet_funct(*ep);
82 elem_mat_provider(100, boundary, modified_penalty);
85 Eigen::VectorXd rhs = Eigen::VectorXd::Zero(dofh.
NumDofs());
93 const auto &entity = dofh.
Entity(idx);
102 Eigen::SparseMatrix<double> As = A.makeSparse();
103 Eigen::SparseLU<Eigen::SparseMatrix<double>> solver;
105 return solver.solve(rhs);
121Eigen::VectorXd solveNestedCylindersNonzeroBC(
122 const std::shared_ptr<const lf::mesh::Mesh> &mesh,
124 double omega2,
bool modified_penalty) {
126 auto f = [](
const Eigen::Vector2d & ) {
127 return Eigen::Vector2d::Zero();
130 const double eps = 1e-10;
131 auto dirichlet_funct = [&](
const lf::mesh::Entity &edge) -> Eigen::Vector2d {
132 const auto *
const geom = edge.Geometry();
133 const auto vertices = geom->Global(edge.RefEl().NodeCoords());
134 if (vertices.col(0).norm() <= R + eps &&
135 vertices.col(0).norm() >= R - eps &&
136 vertices.col(1).norm() <= R + eps &&
137 vertices.col(1).norm() >= R - eps) {
138 return omega2 * R * (vertices.col(1) - vertices.col(0)).normalized();
140 if (vertices.col(0).norm() <= r + eps &&
141 vertices.col(0).norm() >= r - eps &&
142 vertices.col(1).norm() <= r + eps &&
143 vertices.col(1).norm() >= r - eps) {
144 return omega1 * r * (vertices.col(0) - vertices.col(1)).normalized();
146 return Eigen::Vector2d::Zero();
150 for (
const auto *ep : mesh->Entities(1)) {
151 dirichlet(*ep) = dirichlet_funct(*ep);
159 elem_mat_provider(100, boundary, modified_penalty);
162 Eigen::VectorXd rhs = Eigen::VectorXd::Zero(dofh.
NumDofs());
172 std::vector<lf::base::size_type> dofmap(dofh.
NumDofs());
175 const auto &entity = dofh.
Entity(dof);
176 const auto *
const geom = entity.
Geometry();
178 if (geom->Global(entity.RefEl().NodeCoords()).norm() > R - eps) {
179 dofmap[dof] = M_outer;
181 dofmap[dof] = M_inner;
188 std::for_each(A.triplets().begin(), A.triplets().end(),
189 [&](Eigen::Triplet<double> &trip) {
190 trip = Eigen::Triplet<double>(
191 dofmap[trip.row()], dofmap[trip.col()], trip.value());
194 Eigen::VectorXd rhs_mapped = Eigen::VectorXd::Zero(idx);
196 rhs_mapped[dofmap[dof]] += rhs[dof];
201 return {idx == M_outer, 0};
206 Eigen::SparseMatrix<double> As_mapped = A.makeSparse().block(0, 0, idx, idx);
207 Eigen::SparseLU<Eigen::SparseMatrix<double>> solver;
208 solver.compute(As_mapped);
209 const Eigen::VectorXd sol_mapped = solver.solve(rhs_mapped);
213 Eigen::VectorXd sol = Eigen::VectorXd::Zero(dofh.
NumDofs());
215 sol[dof] = sol_mapped[dofmap[dof]];
227template <
typename... Args>
228static std::string concat(Args &&... args) {
229 std::ostringstream ss;
247int main(
int argc,
char *argv[]) {
248 const double r = 0.25;
250 const double omega1 = 0;
251 const double omega2 = 1;
254 std::string mesh_selection;
255 boost::program_options::options_description desc{
"Options"};
256 desc.add_options()(
"help,h",
"Help Screen")(
257 "type", boost::program_options::value<std::string>(&mesh_selection),
258 "Type of mesh to use. Either 'builder', 'files' or 'irregular'");
259 boost::program_options::positional_options_description pos_desc;
260 pos_desc.add(
"type", 1);
261 boost::program_options::command_line_parser parser{argc, argv};
262 parser.options(desc).positional(pos_desc).allow_unregistered();
263 boost::program_options::parsed_options po = parser.run();
264 boost::program_options::variables_map vm;
265 boost::program_options::store(po, vm);
266 boost::program_options::notify(vm);
267 if (vm.count(
"help") != 0U) {
268 std::cout << desc << std::endl;
271 std::vector<std::shared_ptr<lf::mesh::Mesh>> meshes;
272 if (mesh_selection ==
"files") {
274 std::filesystem::path meshpath = __FILE__;
275 meshpath = meshpath.parent_path();
276 for (
int i = 0; i <= 4; ++i) {
277 const auto meshfile = meshpath / concat(
"annulus", std::setw(2),
278 std::setfill(
'0'), i,
".msh");
279 std::unique_ptr<lf::mesh::MeshFactory> factory =
280 std::make_unique<lf::mesh::hybrid2d::MeshFactory>(2);
282 meshes.push_back(reader.mesh());
284 }
else if (mesh_selection ==
"builder") {
286 for (
unsigned i = 0U; i < 8U; ++i) {
287 const unsigned nx = 4U << i;
288 const double dx = 2 * M_PI * (r + R) / 2 / nx;
289 const unsigned ny = std::max(
static_cast<unsigned>((R - r) / dx), 1U);
292 std::unique_ptr<lf::mesh::MeshFactory> factory =
293 std::make_unique<lf::mesh::hybrid2d::MeshFactory>(2);
296 builder.setInnerRadius(r);
297 builder.setOuterRadius(R);
298 builder.setNumAngularCells(nx);
299 builder.setNumRadialCells(ny);
300 meshes.push_back(builder.Build());
302 }
else if (mesh_selection ==
"irregular") {
303 std::filesystem::path meshpath = __FILE__;
304 const auto mesh_irregular_path =
305 meshpath.parent_path() /
"annulus_irregular.msh";
306 const auto mesh_irregular_inverted_path =
307 meshpath.parent_path() /
"annulus_irregular_inverted.msh";
308 std::unique_ptr<lf::mesh::MeshFactory> factory_irregular =
309 std::make_unique<lf::mesh::hybrid2d::MeshFactory>(2);
310 std::unique_ptr<lf::mesh::MeshFactory> factory_irregular_inverted =
311 std::make_unique<lf::mesh::hybrid2d::MeshFactory>(2);
313 mesh_irregular_path.string());
315 std::move(factory_irregular_inverted),
316 mesh_irregular_inverted_path.string());
317 meshes.push_back(reader_irregular.mesh());
318 meshes.push_back(reader_irregular_inverted.mesh());
320 std::cout << desc << std::endl;
325 const double C1 = 2 * (omega1 * r * r - omega2 * R * R) / (r * r - R * R);
326 const double C2 = ((omega1 - omega2) * r * r * R * R) / (R * R - r * r);
327 auto analytic_velocity = [&](
const Eigen::Vector2d &x) -> Eigen::Vector2d {
328 const double radius = x.norm();
332 return -(0.5 * C1 * radius + C2 / radius) * vec;
334 auto analytic_gradient = [&](
const Eigen::Vector2d &x) -> Eigen::Matrix2d {
335 const double r2 = x.squaredNorm();
337 g << 2 * C2 * x[0] * x[1] / r2 / r2,
338 -C1 / 2 - (C2 * r2 - 2 * C2 * x[1] * x[1]) / r2 / r2,
339 C1 / 2 + (C2 * r2 - 2 * C2 * x[0] * x[0]) / r2 / r2,
340 -2 * C2 * x[0] * x[1] / r2 / r2;
345 for (
const auto &mesh : meshes) {
349 const auto fe_space =
350 std::make_shared<lf::uscalfe::FeSpaceLagrangeO1<double>>(mesh);
351 const Eigen::VectorXd solution_zero =
352 solveNestedCylindersZeroBC(mesh, dofh, r, R, omega1, omega2,
false);
353 const Eigen::VectorXd solution_nonzero =
354 solveNestedCylindersNonzeroBC(mesh, dofh, r, R, omega1, omega2,
false);
355 const Eigen::VectorXd solution_zero_modified =
356 solveNestedCylindersZeroBC(mesh, dofh, r, R, omega1, omega2,
true);
357 const Eigen::VectorXd solution_nonzero_modified =
358 solveNestedCylindersNonzeroBC(mesh, dofh, r, R, omega1, omega2,
true);
360 const auto velocity_exact =
362 const auto grad_exact =
364 const auto velocity_zero =
367 fe_space, solution_zero);
368 const auto velocity_nonzero =
371 fe_space, solution_nonzero);
372 const auto velocity_zero_modified =
375 fe_space, solution_zero_modified);
376 const auto velocity_nonzero_modified =
379 fe_space, solution_nonzero_modified);
382 mesh, concat(
"result_zero_", dofh.
NumDofs(),
".vtk"));
384 mesh, concat(
"result_nonzero_", dofh.
NumDofs(),
".vtk"));
385 writer_zero.WriteCellData(
"velocity", velocity_zero);
386 writer_zero.WriteCellData(
"velocity_modified", velocity_zero_modified);
387 writer_nonzero.WriteCellData(
"velocity", velocity_nonzero);
388 writer_nonzero.WriteCellData(
"velocity_modified",
389 velocity_nonzero_modified);
390 writer_zero.WriteCellData(
392 writer_nonzero.WriteCellData(
395 auto diff_velocity_zero = velocity_zero - velocity_exact;
396 auto diff_velocity_zero_modified = velocity_zero_modified - velocity_exact;
397 auto diff_velocity_nonzero = velocity_nonzero - velocity_exact;
398 auto diff_velocity_nonzero_modified =
399 velocity_nonzero_modified - velocity_exact;
400 auto diff_gradient_zero = -grad_exact;
401 auto diff_gradient_zero_modified = -grad_exact;
402 auto diff_gradient_nonzero = -grad_exact;
403 auto diff_gradient_nonzero_modified = -grad_exact;
408 mesh, diff_velocity_zero, qr_provider);
410 mesh, diff_velocity_nonzero, qr_provider);
413 mesh, diff_velocity_zero, diff_gradient_zero, qr_provider);
415 mesh, diff_velocity_nonzero, diff_gradient_nonzero, qr_provider);
416 const double L2_zero_modified =
418 mesh, diff_velocity_zero_modified, qr_provider);
419 const double L2_nonzero_modified =
421 mesh, diff_velocity_nonzero_modified, qr_provider);
422 const double DG_zero_modified =
424 mesh, diff_velocity_zero_modified, diff_gradient_zero_modified,
426 const double DG_nonzero_modified =
428 mesh, diff_velocity_nonzero_modified,
429 diff_gradient_nonzero_modified, qr_provider);
430 std::cout << mesh->NumEntities(2) <<
' ' << L2_zero <<
' ' << DG_zero <<
' '
431 << L2_nonzero <<
' ' << DG_nonzero <<
' ' << L2_zero_modified
432 <<
' ' << DG_zero_modified <<
' ' << L2_nonzero_modified <<
' '
433 << DG_nonzero_modified << std::endl;
A temporary data structure for matrix in COO format.
A general (interface) class for DOF handling, see Lecture Document Paragraph 2.7.4....
virtual const lf::mesh::Entity & Entity(gdof_idx_t dofnum) const =0
retrieve unique entity at which a basis function is located
virtual size_type NumDofs() const =0
total number of dof's handled by the object
static constexpr RefEl kSegment()
Returns the (1-dimensional) reference segment.
static constexpr RefEl kPoint()
Returns the (0-dimensional) reference point.
Reads a Gmsh *.msh file into a mesh::MeshFactory and provides a link between mesh::Entity objects and...
Write a mesh along with mesh data into a vtk file.
Interface class representing a topological entity in a cellular complex
virtual const geometry::Geometry * Geometry() const =0
Describes the geometry of this entity.
A MeshDataSet that attaches data of type T to every entity of a mesh that has a specified codimension...
MeshFunction wrapper for a simple function of physical coordinates.
Element matrix provider for the stokes system.
Element vector provider for the stokes system.
A mesh builder for disks with a hole in the middle.
A MeshFunction returning the velocity computed from the basis function coefficients of a vector poten...
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.
void AssembleVectorLocally(dim_t codim, const DofHandler &dof_handler, ENTITY_VECTOR_PROVIDER &entity_vector_provider, VECTOR &resultvector)
entity-local assembly of (right-hand-side) vectors from element vectors
unsigned int size_type
general type for variables related to size of arrays
QuadRule make_TriaQR_MidpointRule()
midpoint quadrature rule for triangles
void FixFlaggedSolutionComponents(SELECTOR &&selectvals, COOMatrix< SCALAR > &A, RHSVECTOR &b)
enforce prescribed solution components
@ kPoint
Returns the (0-dimensional) reference point.
CodimMeshDataSet< bool > flagEntitiesOnBoundary(const std::shared_ptr< const Mesh > &mesh_p, lf::base::dim_t codim)
flag entities of a specific co-dimension located on the boundary
QuadRule make_QuadRule(base::RefEl ref_el, unsigned degree)
Returns a QuadRule object for the given Reference Element and Degree.
double DGnorm(const std::shared_ptr< const lf::mesh::Mesh > &mesh, const MF_F &f, const MF_GRAD &f_grad, const QR_SELECTOR &qr_selector)
Compute the DG-norm of a vector valued function over a mesh.
double L2norm(const std::shared_ptr< const lf::mesh::Mesh > &mesh, const MF &f, const QR_SELECTOR &qr_selector)
Compute the -norm of a vector valued function over a mesh.