HArD::Core2D
Hybrid Arbitrary Degree::Core 2D - Library to implement 2D schemes with edge and cell polynomials as unknowns
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 
37 namespace 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
59 public:
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,
68  tensor_function_type kappa,
69  size_t deg_kappa,
70  source_function_type source,
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 
89 private:
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 
118 LEPNC_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
142 Eigen::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();
156  QuadratureRule quadF = generate_quadrature_rule(*edge, 5);
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 
250 Eigen::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 
281 Eigen::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);
315  QuadratureRule quadF = generate_quadrature_rule(*edge, 5);
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 
336 double 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 
357 double 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
function[maxeig, mineig, cond, mat]
Definition: compute_eigs.m:1
end
Definition: compute_eigs.m:12
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