HArD::Core2D
Hybrid Arbitrary Degree::Core 2D - Library to implement 2D schemes with edge and cell polynomials as unknowns
Loading...
Searching...
No Matches
LEPNC_diffusion.hpp
Go to the documentation of this file.
1// Implementation of the Locally Enriched Polytopal Non-Conforming scheme in 2D for the diffusion model
2//
3// { - div(K \grad(zeta(u))) = f, inside Omega
4// { K \grad(zeta(u)) . nTF = g, on GammaN
5// { u = g, on GammaD
6//
7// At the moment, only pure Neumann or pure Dirichlet
8//
9// Author: Jerome Droniou (jerome.droniou@monash.edu)
10//
11
12#ifndef _LEPNC_DIFFUSION_HPP
13#define _LEPNC_DIFFUSION_HPP
14
15#include <functional>
16#include <utility>
17#include <iostream>
18
19#include <boost/timer/timer.hpp>
20
21// Matrices and linear solvers
22#include <Eigen/Sparse>
23#include <Eigen/Dense>
24//#include "Eigen/MA41.cpp"
25
26#include "mesh.hpp"
27#include "lepnccore.hpp"
28#include "quad2d.hpp"
29#include "TestCase/TestCase.hpp"
31
37namespace HArDCore2D {
38
43// ----------------------------------------------------------------------------
44// Class definition
45// ----------------------------------------------------------------------------
46/*
47* If using this code in a scientific publication, please cite the reference for the LEPNC:
48*
49* Non-conforming finite elements on polytopal mesh,
50* J. Droniou, R. Eymard, T. Gallouët and R. Herbin
51* url:
52*
53*/
54
57
58// Types
59public:
60 using solution_function_type = std::function<double(double,double)>;
61 using source_function_type = std::function<double(double,double,Cell*)>;
62 using grad_function_type = std::function<VectorRd(double,double,Cell*)>;
63 using tensor_function_type = std::function<Eigen::Matrix2d(double,double,Cell*)>;
64
67 LEPNCCore &nc,
69 size_t deg_kappa,
72 solution_function_type exact_solution,
73 grad_function_type grad_exact_solution,
74 std::string solver_type,
75 std::ostream & output = std::cout
76 );
77
79 Eigen::VectorXd solve();
80
82 double EnergyNorm(const Eigen::VectorXd Xh) const;
83
84 double get_assembly_time() const;
85 double get_solving_time() const;
86 double get_solving_error() const;
87 double get_itime(size_t idx) const;
88
89private:
91 Eigen::MatrixXd diffusion_operator(const size_t iT) const;
92
94 Eigen::VectorXd load_operator(const size_t iT) const;
95
96 const LEPNCCore& nc;
97 const tensor_function_type kappa;
98 size_t _deg_kappa;
99 const source_function_type source;
100 const BoundaryConditions m_BC;
101 const solution_function_type exact_solution;
102 const grad_function_type grad_exact_solution;
103 const std::string solver_type;
104 std::ostream & m_output;
105
106 // To store local diffusion and source terms
107 std::vector<Eigen::MatrixXd> DiffT;
108 std::vector<Eigen::VectorXd> SourceT;
109
110 // Computation statistics
111 size_t _assembly_time;
112 size_t _solving_time;
113 double _solving_error;
114 mutable std::vector<size_t> _itime = std::vector<size_t>(10, 0);
115
116};
117
118LEPNC_diffusion::LEPNC_diffusion(LEPNCCore &nc, tensor_function_type kappa, size_t deg_kappa, source_function_type source, BoundaryConditions BC, solution_function_type exact_solution, grad_function_type grad_exact_solution, std::string solver_type, std::ostream & output)
119 : nc(nc),
120 kappa(kappa),
121 _deg_kappa(deg_kappa),
122 source(source),
123 m_BC(BC),
124 exact_solution(exact_solution),
125 grad_exact_solution(grad_exact_solution),
126 solver_type(solver_type),
127 m_output(output) {
128
129 m_output << "[LEPNC_diffusion] Initializing" << std::endl;
130
131 //---- Compute diffusion, mass matrix and source (do not change during Newton) ------//
132 const Mesh* mesh = nc.get_mesh();
133 DiffT.resize(mesh->n_cells());
134 SourceT.resize(mesh->n_cells());
135 for (size_t iT = 0; iT < mesh->n_cells(); iT++) {
136 DiffT[iT] = diffusion_operator(iT);
137 SourceT[iT] = load_operator(iT);
138 }
139}
140
141// ASSEMBLE AND SOLVE THE SCHEME
142Eigen::VectorXd LEPNC_diffusion::solve() {
143
144 boost::timer::cpu_timer timer;
145 boost::timer::cpu_timer timerint;
146 const auto mesh = nc.get_mesh();
147 size_t n_edges_dofs = mesh->n_edges();
148 size_t n_cell_dofs = mesh->n_cells() * DimPoly<Cell>(1);
149
150 // Vector of Dirichlet BC, either on u (if weight>0) or zeta(u) (if weight=0)
151 Eigen::VectorXd DirBC = Eigen::VectorXd::Zero(n_cell_dofs + n_edges_dofs);
152 for (size_t ibF = 0; ibF < mesh->n_b_edges(); ibF++){
153 Edge* edge = mesh->b_edge(ibF);
154 if (m_BC.type(*edge)=="dir"){
155 size_t iF = edge->global_index();
157 for (QuadratureNode quadrule : quadF){
158 DirBC(n_cell_dofs + iF) += quadrule.w * exact_solution(quadrule.x, quadrule.y);
159 }
160 // Take average
161 DirBC(n_cell_dofs + iF) /= mesh->b_edge(ibF)->measure();
162 }
163 }
164
165
166 // ---- RESOLUTION ------ //
167
168 // System matrix and RHS (only on the edge dofs)
169 Eigen::SparseMatrix<double> GlobMat(n_edges_dofs, n_edges_dofs);
170 std::vector<Eigen::Triplet<double>> triplets_GlobMat;
171 Eigen::VectorXd GlobRHS = Eigen::VectorXd::Zero(n_edges_dofs);
172
173 // Static condensation: matrix and source to recover cell unknowns
174 Eigen::SparseMatrix<double> ScMat(n_cell_dofs, n_edges_dofs);
175 std::vector<Eigen::Triplet<double>> triplets_ScMat;
176 Eigen::VectorXd ScRHS = Eigen::VectorXd::Zero(n_cell_dofs);
177
178 // Assemble local contributions
179 for (size_t iT = 0; iT < mesh->n_cells(); iT++) {
180 Cell* cell = mesh->cell(iT);
181 size_t n_edgesT = cell->n_edges();
182
183 // Local static condensation of element unknowns
184 Eigen::MatrixXd MatT = DiffT[iT];
185 Eigen::MatrixXd ATT = MatT.topLeftCorner(DimPoly<Cell>(1), DimPoly<Cell>(1));
186 Eigen::MatrixXd ATF = MatT.topRightCorner(DimPoly<Cell>(1), n_edgesT);
187 Eigen::MatrixXd AFT = MatT.bottomLeftCorner(n_edgesT, DimPoly<Cell>(1));
188 Eigen::MatrixXd AFF = MatT.bottomRightCorner(n_edgesT, n_edgesT);
189
190 Eigen::PartialPivLU<Eigen::MatrixXd> invATT;
191 invATT.compute(ATT);
192
193 Eigen::MatrixXd invATT_ATF = invATT.solve(ATF);
194 Eigen::VectorXd invATT_source_cell = invATT.solve(SourceT[iT].head(DimPoly<Cell>(1)));
195
196 // Local matrix and right-hand side on the face unknowns
197 Eigen::MatrixXd MatF = Eigen::MatrixXd::Zero(n_edgesT, n_edgesT);
198 Eigen::VectorXd bF = Eigen::VectorXd::Zero(n_edgesT);
199 MatF = AFF - AFT * invATT_ATF;
200 bF = SourceT[iT].tail(n_edgesT) - AFT * invATT_source_cell;
201
202 // Assemble static condensation operator
203 ScRHS.segment(iT * DimPoly<Cell>(1), DimPoly<Cell>(1)) = invATT_source_cell;
204 for (size_t i = 0; i < DimPoly<Cell>(1); i++){
205 size_t iGlobal = iT * DimPoly<Cell>(1) + i;
206 for (size_t j = 0; j < n_edgesT; j++){
207 size_t jGlobal = cell->edge(j)->global_index();
208 triplets_ScMat.emplace_back(iGlobal, jGlobal, invATT_ATF(i, j));
209 }
210 }
211
212 // GLOBAL MATRIX and RHS on the edge unknowns, using the matrices MatF obtained after static condensation
213 for (size_t i = 0; i < n_edgesT; i++){
214 size_t iGlobal = cell->edge(i)->global_index();
215 for (size_t j = 0; j < n_edgesT; j++){
216 size_t jGlobal = cell->edge(j)->global_index();
217 triplets_GlobMat.emplace_back(iGlobal, jGlobal, MatF(i, j));
218 }
219 GlobRHS(iGlobal) += bF(i);
220 }
221 }
222
223 // Assemble the global linear system (without BC), and matrix to recover statically-condensed cell dofs
224 GlobMat.setFromTriplets(std::begin(triplets_GlobMat), std::end(triplets_GlobMat));
225 ScMat.setFromTriplets(std::begin(triplets_ScMat), std::end(triplets_ScMat));
226
227 // Dirichlet boundary conditions
228 size_t n_edge_unknowns = mesh->n_edges() - m_BC.n_dir_edges();
229 Eigen::SparseMatrix<double> A = GlobMat.topLeftCorner(n_edge_unknowns, n_edge_unknowns);
230 Eigen::VectorXd B = GlobRHS.head(n_edge_unknowns);
231
232 // Solve condensed system and recover cell unknowns.
233 Eigen::BiCGSTAB<Eigen::SparseMatrix<double> > solver;
234 solver.compute(A);
235 Eigen::VectorXd Xh_edges = solver.solve(B);
236
237 Eigen::VectorXd Xh = DirBC;
238 Xh.segment(n_cell_dofs, n_edge_unknowns) = Xh_edges;
239 Xh.head(n_cell_dofs) = ScRHS - ScMat * Xh.tail(n_edges_dofs);
240
241
242 return Xh;
243}
244
245
246//********************************
247// local diffusion matrix
248//********************************
249
250Eigen::MatrixXd LEPNC_diffusion::diffusion_operator(const size_t iT) const {
251
252 const auto mesh = nc.get_mesh();
253 Cell* cell = mesh->cell(iT);
254 const size_t n_edgesT = cell->n_edges();
255
256 // Total number of degrees of freedom local to this cell
257 size_t local_dofs = n_edgesT + DimPoly<Cell>(1);
258
259 // Diffusion in the cell.
260 std::function<Eigen::Matrix2d(double,double)> kappaT = [&](double x, double y){
261 return kappa(x, y, cell);
262 };
263
264 // Compute cell quadrature nodes and values of cell basis functions and gradients at these nodes
265 size_t doe = 2 + _deg_kappa;
266 QuadratureRule quadT = generate_quadrature_rule(*cell, doe, true);
267 std::vector<Eigen::ArrayXXd> Dnc_phiT_quadT = nc.grad_nc_basis_quad(iT, quadT);
268 // Diffusion tensor at the quadrature nodes
269 std::vector<Eigen::Matrix2d> kappaT_quadT(quadT.size());
270 std::transform(quadT.begin(), quadT.end(), kappaT_quadT.begin(),
271 [&kappaT](QuadratureNode qr) -> Eigen::MatrixXd { return kappaT(qr.x, qr.y); });
272
273 return nc.gram_matrix(Dnc_phiT_quadT, Dnc_phiT_quadT, local_dofs, local_dofs, quadT, true, kappaT_quadT);
274}
275
276
277//********************************
278// local load term
279//********************************
280
281Eigen::VectorXd LEPNC_diffusion::load_operator(const size_t iT) const {
282 // Load for the cell DOFs (first indices) and face DOFs (last indices)
283 const auto mesh = nc.get_mesh();
284 Cell* cell = mesh->cell(iT);
285 size_t n_edgesT = cell->n_edges();
286 size_t local_dofs = DimPoly<Cell>(1) + n_edgesT;
287 Eigen::VectorXd b = Eigen::VectorXd::Zero(local_dofs);
288
289 // Quadrature nodes and values of cell basis functions at these nodes
290 size_t doe = 5;
291 QuadratureRule quadT = generate_quadrature_rule(*cell, doe, true);
292 size_t nbq = quadT.size();
293 std::vector<Eigen::ArrayXd> nc_phiT_quadT = nc.nc_basis_quad(iT, quadT);
294
295 // Value of source times quadrature weights at the quadrature nodes
296 Eigen::ArrayXd weight_source_quad = Eigen::ArrayXd::Zero(nbq);
297 for (size_t iqn = 0; iqn < nbq; iqn++){
298 weight_source_quad(iqn) = quadT[iqn].w * source(quadT[iqn].x, quadT[iqn].y, cell);
299 }
300
301 for (size_t i=0; i < local_dofs; i++){
302 b(i) = (weight_source_quad * nc_phiT_quadT[i]).sum();
303 }
304
305 // Boundary values, if we have a boundary cell and Neumann edges
306 if (cell->is_boundary()){
307 // Boundary values only on boundary Neumann edges
308 for (size_t ilF = 0; ilF < cell->n_edges(); ilF++) {
309 Edge* edge = cell->edge(ilF);
310 if (m_BC.type(*edge)=="neu"){
311 // BC on boundary faces
312 if (edge->is_boundary()){
313 const auto& nTF = cell->edge_normal(ilF);
314 const auto& phi_i = nc.nc_basis(iT, DimPoly<Cell>(1) + ilF);
316 std::function<double(VectorRd)> Kgrad_n = [&](VectorRd p){
317 return nTF.dot(kappa(p.x(),p.y(),cell) * grad_exact_solution(p.x(),p.y(),cell)) * phi_i(p.x(),p.y());
318 };
319 for (QuadratureNode qr : quadF){
320 b(DimPoly<Cell>(1) + ilF) += qr.w * Kgrad_n(qr.vector());
321 }
322 }
323 }
324 }
325 }
326
327 return b;
328}
329
330
331
332//*******************************************************
333// Norms: L2 mass lumped, Energy
334//*******************************************************
335
336double LEPNC_diffusion::EnergyNorm(const Eigen::VectorXd Xh) const {
337 const Mesh* mesh = nc.get_mesh();
338 double value = 0.0;
339
340 for (size_t iT = 0; iT < mesh-> n_cells(); iT++) {
341 Eigen::VectorXd XTF = nc.nc_restr(Xh, iT);
342 value += XTF.transpose() * DiffT[iT] * XTF;
343 }
344
345 return sqrt(value);
346}
347
348
350 return double(_assembly_time) * pow(10, -9);
351}
352
354 return double(_solving_time) * pow(10, -9);
355}
356
357double LEPNC_diffusion::get_itime(size_t idx) const {
358 return double(_itime[idx]) * pow(10, -9);
359}
360
362 return _solving_error;
363}
364
366} // end of namespace HArDCore2D
367
368#endif //_LEPNC_DIFFUSION_HPP
The BoundaryConditions class provides definition of boundary conditions.
Definition BoundaryConditions.hpp:45
const std::string type(const Edge &edge) const
Test the boundary condition of an edge.
Definition BoundaryConditions.cpp:41
const size_t n_dir_edges() const
Returns the number of Dirichlet edges.
Definition BoundaryConditions.hpp:70
Definition lepnccore.hpp:33
The LEPNC_diffusion class provides an implementation of the LEPNC method for the stationnary diffusio...
Definition LEPNC_diffusion.hpp:56
Definition Mesh2D.hpp:26
Compute max and min eigenvalues of all matrices for i
Definition compute_eigs.m:5
Eigen::Vector2d VectorRd
Definition basis.hpp:55
const size_t DimPoly< Cell >(const int m)
Compute the size of the basis of 2-variate polynomials up to degree m.
Definition hybridcore.hpp:63
const Mesh * get_mesh() const
Returns a pointer to the mesh.
Definition hybridcore.hpp:201
std::function< VectorRd(double, double, Cell *)> grad_function_type
type for gradient
Definition LEPNC_diffusion.hpp:62
Eigen::MatrixXd gram_matrix(const std::vector< Eigen::ArrayXd > &f_quad, const std::vector< Eigen::ArrayXd > &g_quad, const size_t &nrows, const size_t &ncols, const QuadratureRule &quad, const bool &sym, std::vector< double > L2weight={}) const
Definition lepnccore.hpp:517
double get_itime(size_t idx) const
various intermediate assembly times
Definition LEPNC_diffusion.hpp:357
std::function< Eigen::Matrix2d(double, double, Cell *)> tensor_function_type
type for diffusion tensor
Definition LEPNC_diffusion.hpp:63
const std::vector< Eigen::ArrayXd > nc_basis_quad(const size_t iT, const QuadratureRule quad) const
Computes non-conforming basis functions at the given quadrature nodes.
Definition lepnccore.hpp:432
Eigen::VectorXd nc_restr(const Eigen::VectorXd &Xh, size_t iT) const
Extract from a global vector Xh of unknowns the non-conforming unknowns corresponding to cell iT.
Definition lepnccore.hpp:678
const std::vector< Eigen::ArrayXXd > grad_nc_basis_quad(const size_t iT, const QuadratureRule quad) const
Compute at the quadrature nodes, where are the cell basis functions.
Definition lepnccore.hpp:471
double get_solving_error() const
residual after solving the scheme
Definition LEPNC_diffusion.hpp:361
double get_solving_time() const
cpu time to solve the scheme
Definition LEPNC_diffusion.hpp:353
std::function< double(double, double)> solution_function_type
type for solution
Definition LEPNC_diffusion.hpp:60
LEPNC_diffusion(LEPNCCore &nc, tensor_function_type kappa, size_t deg_kappa, source_function_type source, BoundaryConditions BC, solution_function_type exact_solution, grad_function_type grad_exact_solution, std::string solver_type, std::ostream &output=std::cout)
Constructor of the class.
Definition LEPNC_diffusion.hpp:118
const cell_basis_type & nc_basis(size_t iT, size_t i) const
Return a reference to the i'th non-conforming basis function of the cell iT.
Definition lepnccore.hpp:410
Eigen::VectorXd solve()
Assemble and solve the scheme.
Definition LEPNC_diffusion.hpp:142
double get_assembly_time() const
cpu time to assemble the scheme
Definition LEPNC_diffusion.hpp:349
std::function< double(double, double, Cell *)> source_function_type
type for source
Definition LEPNC_diffusion.hpp:61
double EnergyNorm(const Eigen::VectorXd Xh) const
Discrete energy norm (associated to the diffusion operator)
Definition LEPNC_diffusion.hpp:336
Polytope< DIMENSION > Cell
Definition Polytope2D.hpp:151
Polytope< 1 > Edge
A Face is a Polytope with object_dim = DIMENSION - 1.
Definition Polytope2D.hpp:147
std::size_t n_cells() const
number of cells in the mesh.
Definition Mesh2D.hpp:63
std::size_t n_edges() const
number of edges in the mesh.
Definition Mesh2D.hpp:61
std::vector< QuadratureNode > QuadratureRule
Definition quadraturerule.hpp:55
QuadratureRule generate_quadrature_rule(const Cell &T, const int doe, const bool force_split)
Generate quadrature rule on mesh element.
Definition quadraturerule.cpp:10
A
Definition mmread.m:102
for j
Definition mmread.m:174
Definition ddr-klplate.hpp:27
Description of one node and one weight from a quadrature rule.
Definition quadraturerule.hpp:41