PolyDiM
C++ library for POLYtopal DIscretization Methods
Loading...
Searching...
No Matches
Examples

This section provides and explains the workflow of a standard example that illustrate how to use the PolyDiM library. Each provided example demonstrates the capabilities of the PolyDiM base classes when solving partial differential equations in one, two, or three spatial dimensions.

The available base classes are organized into the following namespaces:

  • PCC: Primal Conforming method [3] [14] [7].
  • MCC: Mixed Conforming method [2] [5] [6].
  • DF_PCC: Divergence-Free Primal Conforming method [4].

See [9] for a complete description of the library and its theoretical foundations.

Available C++ examples

  • Elliptic_PCC_2D
  • Elliptic_PCC_1D
  • Elliptic_PCC_3D
  • Brinkman_DF_PCC_2D
  • Stokes_DF_PCC_3D
  • NavierStokes_DF_PCC_2D
  • Elastic_PCC_2D
  • Parabolic_PCC_2D
  • Parabolic_PCC_BulkFace_2D
  • Elliptic_MCC_2D
  • Elliptic_MCC_3D

Each example name follows the pattern <Problem>_<LocalSpace>_<Dimension>:

  • Problem — the PDE being solved: Elliptic, Parabolic, Stokes, NavierStokes, Brinkman, Elastic, and so on.
  • Local space — the family of local spaces used;
  • Dimension — the problem geometric dimension: 1D, 2D or 3D.

In the following, we explain step by step the structure of the Elliptic_PCC_2D example. All the other examples follow a similar organization.

Mesh generation

The program first generates the computational mesh and exports it in VTK format for visualization. Subsequently, the geometric quantities required to define the selected local basis functions are computed through the following code:

// CreateMesh
Gedim::MeshMatricesDAO mesh(meshData);
Polydim::examples::Elliptic_PCC_2D::program_utilities::create_domain_mesh(config, domain, mesh);
// Export the domain mesh
{
Gedim::MeshUtilities meshUtilities;
meshUtilities.ExportMeshToVTU(mesh, exportVtuFolder, "Domain_Mesh");
}
// Compute Geometric Properties
const auto meshGeometricData =
Polydim::examples::Elliptic_PCC_2D::program_utilities::create_domain_mesh_geometric_properties(config, mesh);
Definition IMeshDAO.hpp:1068
MeshUtilities.
Definition MeshUtilities.hpp:23
void ExportMeshToVTU(const Gedim::IMeshDAO &mesh, const std::string &exportFolder, const std::string &fileName, const bool &separateFile=false) const
Export Mesh To VTU.
Definition MeshUtilities.cpp:243
Definition MeshMatrices.hpp:21

The mesh object stores the following information:

  • Vertices (0D cells): identifier, coordinates, and marker. The marker is an integer used to associate boundary conditions with the corresponding vertex. By convention, a marker equal to 0 denotes an interior vertex, i.e., no boundary condition is associated with it.
  • Edges (1D cells): identifier, origin vertex, end vertex, and marker.
  • Faces (2D cells): identifier, number of vertices, list of vertex identifiers, number of edges, list of edge identifiers, and marker. For two-dimensional problems, all faces have marker equal to 0.
  • 3D cells: identifier, number of vertices, list of vertex identifiers, number of edges, list of edge identifiers, number of faces, list of face identifiers, and marker. All 3D cells have marker equal to 0.

An example of mesh marker assignment is shown in the following figure:

PolyDiM relies on the GEometry for DIscretization MEthods (GEDiM) library [8] to manage mesh data structures. GEDiM has been developed by the authors and is fully integrated into PolyDiM.

All provided 2D examples support concave polygonal elements. Three-dimensional concave polyhedral elements are also supported, although they are not enabled by default. To use them, the routines that compute the geometric properties of concave elements must be explicitly invoked.

Problem setting

PolyDiM can solve a broad range of partial differential equations (PDEs), including scalar and vector-valued problems, as well as both linear and nonlinear formulations.

To define a test problem, the following information must be provided:

  • the computational domain, identified by its vertices, (edges, and faces in 3D), which may have arbitrary polygonal or polyhedral shapes;
  • the physical coefficients associated with the problem, such as diffusion, advection, reaction, or viscosity coefficieents along with source terms, including body forces, sinks, gravity terms, etc.;
  • the boundary conditions, including both strongly (essential) and weakly (natural) imposed conditions;
  • the exact solution and its derivatives, when error estimation and convergence analysis are required.

A test case is defined as an implementation of the following interface:

struct I_Test
{
virtual std::map<unsigned int, Polydim::PDETools::DOFs::DOFsManager::MeshDOFsInfo::BoundaryInfo> boundary_info() const = 0;
virtual Eigen::VectorXd diffusion_term(const Eigen::MatrixXd &points) const = 0;
virtual std::array<Eigen::VectorXd, 3> advection_term(const Eigen::MatrixXd &points) const = 0;
virtual Eigen::VectorXd source_term(const Eigen::MatrixXd &points) const = 0;
virtual Eigen::VectorXd strong_boundary_condition(const unsigned int marker, const Eigen::MatrixXd &points) const = 0;
virtual Eigen::VectorXd weak_boundary_condition(const unsigned int marker, const Eigen::MatrixXd &points) const = 0;
virtual Eigen::VectorXd exact_solution(const Eigen::MatrixXd &points) const = 0;
virtual std::array<Eigen::VectorXd, 3> exact_derivative_solution(const Eigen::MatrixXd &points) const = 0;
};
Two-dimensional computational domain.
Definition PDE_Mesh_Utilities.hpp:48

The interface above gathers all the information required to solve a diffusion–advection problem. Each test case provides a specific set of problem data and can be executed on different meshes, using different numerical methods and polynomial orders to assess accuracy and convergence properties. These simulation parameters can be configured through the Parameters.ini file located in the build directory associated with each example.

In particular, boundary conditions are assigned through a C++ map that associates each mesh marker with the corresponding boundary condition type.

For instance, the following map

std::map<unsigned int, Polydim::PDETools::DOFs::DOFsManager::MeshDOFsInfo::BoundaryInfo> boundary_info() const
{
return {{0, {Polydim::PDETools::DOFs::DOFsManager::BoundaryTypes::None, 0}},
{1, {Polydim::PDETools::DOFs::DOFsManager::BoundaryTypes::Strong, 1}},
{2, {Polydim::PDETools::DOFs::DOFsManager::BoundaryTypes::Strong, 1}},
{3, {Polydim::PDETools::DOFs::DOFsManager::BoundaryTypes::None, 0}},
{4, {Polydim::PDETools::DOFs::DOFsManager::BoundaryTypes::Strong, 1}},
{5, {Polydim::PDETools::DOFs::DOFsManager::BoundaryTypes::Strong, 1}},
{6, {Polydim::PDETools::DOFs::DOFsManager::BoundaryTypes::Weak, 2}},
{7, {Polydim::PDETools::DOFs::DOFsManager::BoundaryTypes::Weak, 4}},
{8, {Polydim::PDETools::DOFs::DOFsManager::BoundaryTypes::Strong, 1}}};
}

produces the following output:

The library provides examples for the following classes of boundary value problems.

Elliptic

Given a polytopal domain \(\Omega \subset \mathbb{R}^d\), with \(d=1,2,3\), \begin{equation} \begin{cases} \nabla \cdot (- D \nabla u) = f & \text{in } \Omega,\\ u = g_D & \text{on } \Gamma_D, \\ D \nabla u \cdot \mathbf{n} = g_N & \text{on } \Gamma_N \end{cases} \end{equation}

This problem can be solved using both primal (Elliptic_PCC_1D, Elliptic_PCC_2D, Elliptic_PCC_3D) and mixed (Elliptic_MCC_2D, Elliptic_MCC_3D) formulations. For the primal version of the method, this problem can be also solved with standard finite element method on triangles (tetrahedra) or quadrilateral (hexahedra). Moreover, the Z-FEM methods are also available [10].

For the Elliptic_PCC_2D, an example of how to use SUPG stabilizing is provided for advection-dominated problem.

Elastic

Given a polytopal domain \(\Omega \subset \mathbb{R}^d\), with \(d=2,3\), \begin{equation} \begin{cases} \nabla \cdot \mathbf{\sigma} = f & \text{in } \Omega,\\ \mathbf{u} = g_D & \text{on } \Gamma_D, \\ \mathbf{\sigma} \cdot \mathbf{n} = g_N & \text{on } \Gamma_N \end{cases} \end{equation} where \(u\) is a vector-valued function and \(\mathbf{\sigma} = -\mu \mathbf{\epsilon}(\mathbf{u}) + \lambda (\nabla \cdot \mathbf{u}) \mathbf{I}\).

Brinkman, Stokes and Darcy

Given a polytopal domain \(\Omega \subset \mathbb{R}^d\), with \(d=2,3\), \begin{equation} \begin{cases} \nabla \cdot \mathbf{\sigma} + \mathbf{K}^{-1}\mathbf{u} = f & \text{in } \Omega,\\ \mathbf{u} = g_D & \text{on } \Gamma_D, \\ \mathbf{\sigma} \cdot \mathbf{n} = g_N & \text{on } \Gamma_N \end{cases} \end{equation} where \(u\) is a vector-valued function and \(\mathbf{\sigma} = -\mu \nabla \mathbf{u} + p \mathbf{I}\).

PolyDiM provides also an example to deal with the Navier-Stokes equations.

Parabolic bulk-surface problems

We additionally demonstrate the use of PolyDiM for coupled bulk–surface problems in two spatial dimensions [13]. In particular, we solve the following parabolic coupled problem: \begin{equation} \begin{cases} \frac{\partial u^{2}}{\partial t} - d^2 \Delta u^2 + \gamma^2 u^2 = f^2 &\mathbf{x} \in \Omega,\ t \in [0,T]\\ \frac{\partial u^1}{\partial t} - d^1 \Delta_{\Gamma} u^1 + \gamma^1 u^1 + \nabla u^2 \cdot \mathbf{n} = f^1 &\mathbf{x} \in \Gamma,\ t \in [0,T]\\ \nabla u^2 \cdot \mathbf{n} = -\alpha u^2 + \beta u^1 & \mathbf{x} \in \Gamma,\ t \in [0,T], \end{cases} \label{eq:bulksurfaceproblem} \end{equation} where $\Delta_{\Gamma}$ is the Laplace-Beltrami operator and superscripts $2$ and $1$ refer to bulk and surface data, respectively.

The Degrees of Freedom Manager

The Degrees of Freedom (DOFs) Manager is responsible for numbering all degrees of freedom associated with the discrete problem.

Given a reference element that specifies the number of DOFs attached to each 0D, 1D, 2D, and 3D mesh entity, the DOF Manager assigns consecutive global indices to every mesh entity according to the chosen discretization.

The exact distribution of DOFs depends on the selected base class. Mesh entities on which strong (essential) boundary conditions are imposed do not receive standard DOFs. Instead, the DOF Manager assigns them a separate set of indices, referred to as strong degrees of freedom (STRONGs).

Consequently, the library maintains two independent counters:

  • one for the standard DOFs;
  • one for the STRONGs.

See [9] for further details.

// Create Discrete Space
const auto reference_element_data =
Polydim::examples::Elliptic_PCC_2D::local_space::CreateReferenceElement(config.MethodType(), config.MethodOrder());
const auto reference_element_num_dofs =
Polydim::examples::Elliptic_PCC_2D::local_space::ReferenceElementNumDOFs(reference_element_data);
const auto meshDOFsInfo =
dofManager.Create_Constant_DOFsInfo_2D(mesh_connectivity_data, {reference_element_num_dofs, boundary_info});
const auto dofs_data = dofManager.CreateDOFs_2D(meshDOFsInfo, mesh_connectivity_data);
Builds and manages the degrees of freedom (DOFs) of a discrete space over a mesh.
Definition DOFsManager.hpp:73
DOFsData CreateDOFs_2D(const MeshDOFsInfo &meshDOFsInfo, const mesh_connectivity_data_class &mesh) const
Build the DOF numbering for a 2D problem (vertices, edges and cells).
Definition DOFsManager.hpp:673
Definition MeshMatricesDAO_mesh_connectivity_data.hpp:24

An example of how the DOF managers works is shown in the following figures:

Assembling Phase

The following code produces the global system matrix \(\mathbf{A} \in \mathbb{R}^{N^{\text{DOF}} \times N^{\text{DOF}}}\) and the right-hand side \(\mathbf{b} \in \mathbb{R}^{N^{\text{DOF}}}\), where \(N^{\text{DOF}}\) is the total number of degrees of freedom. A vector \(\mathbf{u}_S \in \mathbb{R}^{N^{\text{STRONG}}} \) is also built that contains the values of strong degrees of freedom, that are computed exactly thanks to the boundary data.

Polydim::examples::Elliptic_PCC_2D::Assembler assembler;
auto assembler_data =
assembler.Assemble(config, mesh, meshGeometricData, meshDOFsInfo, dofs_data, reference_element_data, *test);

Solver

GeDiM provides to PolyDiM different interfaces to linear algebra library (Eigen, Petsc, atc). All solver interfaces are designed to operate with the generic classes Gedim::ISparseArray and Gedim::IArray, which represent the sparse matrices and vectors of the linear system, respectively. This level of abstraction in the linear algebra layer, if used by the user, enables to switch between different solvers (such as EIGEN, PETSC, and LAPACK) with ease and without altering the core components of the code, such as the assembly process.

if (dofs_data.NumberDOFs > 0)
{
Gedim::Output::PrintGenericMessage("Factorize...", true);
solver.Initialize(assembler_data.globalMatrixA);
solver.Solve(assembler_data.rightHandSide, assembler_data.solution);
}
Eigen LU Linear solver.
Definition Eigen_LUSolver.hpp:23
SolutionInfo Solve() const
Compute the solution.
Definition Eigen_LUSolver.cpp:44
void Initialize(const ISparseArray &matrix, const IArray &rightHandSide, IArray &solution, const Configuration &config=Configuration())
Initialize the linear solver Ax = b.
Definition Eigen_LUSolver.cpp:25
static void PrintGenericMessage(const std::string &message, const bool &onlyMaster,...)
Used to print to file, or on screen a generic message.
Definition IOUtilities.cpp:609
static void PrintStatusProgram(const std::string &programStep,...)
Used to print to file, or on screen the status of the program.
Definition IOUtilities.cpp:492
static double StopTime(const std::string &nameTime, const bool &printTime=true)
Stop time with nameTime if exists and print to file the result.
Definition IOUtilities.cpp:1014
static void StartTime(const std::string &nameTime)
Start time with nameTime if not exist.
Definition IOUtilities.cpp:937

Post-proccesing

Each example includes functions for exporting the computed degrees of freedom, the numerical solution, and its derivatives to ParaView. When an exact solution is available, routines for error computation are also provided.

Furthermore, PolyDiM computes statistics related to the accuracy of the elemental matrices, allowing a detailed assessment of the method's performance.

Gedim::Output::PrintGenericMessage("ComputeErrors...", true);
Gedim::Profiler::StartTime("ComputeErrors");
auto post_process_data =
assembler.PostProcessSolution(config, mesh, meshGeometricData, dofs_data, reference_element_data, assembler_data, *test);
Gedim::Profiler::StopTime("ComputeErrors");
Gedim::Output::PrintGenericMessage("ExportSolution...", true);
Gedim::Profiler::StartTime("ExportSolution");
Polydim::examples::Elliptic_PCC_2D::program_utilities::export_solution(config, mesh, dofs_data, assembler_data, post_process_data, exportSolutionFolder, exportVtuFolder);
Polydim::examples::Elliptic_PCC_2D::program_utilities::export_dofs(config,
mesh,
meshGeometricData,
meshDOFsInfo,
dofs_data,
assembler_data,
post_process_data,
exportVtuFolder);
Gedim::Profiler::StopTime("ExportSolution");
Gedim::Output::PrintGenericMessage("ComputeMethodPerformance...", true);
Gedim::Profiler::StartTime("ComputeMethodPerformance");
if (config.ComputeMethodPerformance())
{
const auto performance = assembler.ComputePerformance(config, mesh, meshGeometricData, reference_element_data);
Polydim::examples::Elliptic_PCC_2D::program_utilities::export_performance(config, performance, exportSolutionFolder);
}
Gedim::Profiler::StopTime("ComputeMethodPerformance");
Gedim::Output::PrintStatusProgram("ComputeMethodPerformance");