HArD::Core2D
Hybrid Arbitrary Degree::Core 2D - Library to implement 2D schemes with edge and cell polynomials as unknowns
HHO_LocVarDiff.hpp
Go to the documentation of this file.
1 // Implementation of the HHO scheme in 2D for the diffusion equation
2 //
3 // { -div(K \grad(u)) = f, inside Omega
4 // { K \grad(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 /*
13  *
14  * This implementation of HHO was developped following the principles described in
15  * Appendix B of the book
16  *
17  * The Hybrid High-Order Method for Polytopal Meshes: Design, Analysis, and Applications.
18  * D. A. Di Pietro and J. Droniou. Modeling, Simulation and Applications, vol. 19.
19  * Springer International Publishing, 2020, xxxi + 525p. doi: 10.1007/978-3-030-37203-3.
20  * url: https://hal.archives-ouvertes.fr/hal-02151813.
21  *
22  * If you use this code or part of it for a scientific publication, please cite the book
23  * above as a reference for the implementation.
24  *
25  */
26 
27 #ifndef _HHO_LOCVARDIFF_HPP
28 #define _HHO_LOCVARDIFF_HPP
29 
30 #include <functional>
31 #include <utility>
32 #include <iostream>
33 
34 #include <boost/timer/timer.hpp>
35 
36 // Matrices and linear solvers
37 #include <Eigen/Sparse>
38 #include <Eigen/Dense>
39 //#include "Eigen/MA41.cpp"
40 
41 //#include <vertex.hpp>
42 #include <hybridcore.hpp>
43 #include <elementquad.hpp>
44 #include <parallel_for.hpp>
46 #include "TestCase/TestCase.hpp"
47 
53 namespace HArDCore2D {
54 
59  // ----------------------------------------------------------------------------
60  // Class definition
61  // ----------------------------------------------------------------------------
62 
64 
66 
67  public:
68 
71  HybridCore& hho,
72  size_t K,
73  int L,
74  CellFType<MatrixRd> kappa,
75  size_t deg_kappa,
76  CellFType<double> source,
78  FType<double> exact_solution,
79  CellFType<VectorRd> grad_exact_solution,
80  std::string solver_type,
81  bool use_threads,
82  std::ostream & output = std::cout
83  );
84 
86  void assemble(HybridCore& hho);
87  UVector solve(HybridCore& hho);
88 
90  double EnergyNorm(HybridCore& hho, const UVector Xh);
91 
93  Eigen::SparseMatrix<double> get_SysMat(){
94  return SysMat;
95  }
96 
98  inline double get_assembly_time() const
99  {
100  return double(_assembly_time) * pow(10, -9);
101  };
103  inline double get_solving_time() const
104  {
105  return double(_solving_time) * pow(10, -9);
106  };
108  inline double get_solving_error() const
109  {
110  return _solving_error;
111  };
113  inline double get_itime(size_t idx) const
114  {
115  return double(_itime[idx]) * pow(10, -9);
116  };
117 
119  const inline size_t get_nlocal_cell_dofs() { return m_nlocal_cell_dofs; }
121  const inline size_t get_nlocal_edge_dofs() { return m_nlocal_edge_dofs; }
123  const inline size_t get_nhighorder_dofs() { return m_nhighorder_dofs; }
125  const inline size_t get_ntotal_cell_dofs() { return m_ntotal_cell_dofs; }
127  const inline size_t get_ntotal_edge_dofs() { return m_ntotal_edge_dofs; }
129  const inline size_t get_ndir_edge_dofs() { return m_ndir_edge_dofs; }
131  const inline size_t get_ntotal_dofs() { return m_ntotal_dofs; }
132 
133  private:
135  Eigen::MatrixXd diffusion_operator(HybridCore& hho, const size_t iT, const ElementQuad& elquad) const;
136 
138  Eigen::VectorXd load_operator(HybridCore& hho, const size_t iT, const ElementQuad &elquad) const;
139 
140  // Reference to the HybridCore structure
141  HybridCore& m_hho;
142 
143  // Degrees on edges and in cells
144  size_t m_K;
145  int m_L;
146  size_t m_Ldeg;
147 
148  // Data
149  const CellFType<MatrixRd> kappa;
150  size_t _deg_kappa;
151  const CellFType<double> source;
152  const BoundaryConditions m_BC;
153  const FType<double> exact_solution;
154  const CellFType<VectorRd> grad_exact_solution;
155  const std::string solver_type;
156  const bool m_use_threads;
157  std::ostream & m_output;
158 
159  // DOFs
160  const size_t m_nlocal_cell_dofs;
161  const size_t m_nlocal_edge_dofs;
162  const size_t m_nhighorder_dofs;
163  const size_t m_ntotal_cell_dofs;
164  const size_t m_ntotal_edge_dofs;
165  const size_t m_ndir_edge_dofs;
166  const size_t m_nnondir_edge_dofs;
167  const size_t m_ntotal_dofs;
168 
169  // Local bilinear forms
170  std::vector<Eigen::MatrixXd> aT;
171  // Global matrix (without BC accounted for), and system matrix
172  Eigen::SparseMatrix<double> GlobMat;
173  Eigen::SparseMatrix<double> SysMat;
174  // If static condensation (L>=0): matrix to recover cell unknowns
175  // If barycentric elimination (L=-1): matrix to recover cell unknowns
176  Eigen::SparseMatrix<double> ScBeMat;
177  // Source terms for the system, and for recovering cell unknowns from static condensation
178  Eigen::VectorXd GlobRHS;
179  Eigen::VectorXd ScRHS;
180 
181  // Computation statistics
182  size_t _assembly_time;
183  size_t _solving_time;
184  double _solving_error;
185  mutable std::vector<size_t> _itime = std::vector<size_t>(10, 0);
186 
187  };
188 
189  HHO_LocVarDiff::HHO_LocVarDiff(HybridCore& hho, size_t K, int L, CellFType<MatrixRd> kappa, size_t deg_kappa, CellFType<double> source, BoundaryConditions BC, FType<double> exact_solution, CellFType<VectorRd> grad_exact_solution, std::string solver_type, bool use_threads, std::ostream & output)
190  : m_hho(hho),
191  m_K(K),
192  m_L(L),
193  m_Ldeg(std::max(L,0)),
194  kappa(kappa),
195  _deg_kappa(deg_kappa),
196  source(source),
197  m_BC(BC),
198  exact_solution(exact_solution),
199  grad_exact_solution(grad_exact_solution),
200  solver_type(solver_type),
201  m_use_threads(use_threads),
202  m_output(output),
203  m_nlocal_cell_dofs(DimPoly<Cell>(m_Ldeg)),
204  m_nlocal_edge_dofs(DimPoly<Edge>(m_K)),
205  m_nhighorder_dofs(DimPoly<Cell>(m_K+1)),
206  m_ntotal_cell_dofs(m_nlocal_cell_dofs * m_hho.get_mesh()->n_cells()),
207  m_ntotal_edge_dofs(m_nlocal_edge_dofs * m_hho.get_mesh()->n_edges()),
208  m_ndir_edge_dofs(m_nlocal_edge_dofs * m_BC.n_dir_edges()),
209  m_nnondir_edge_dofs(m_nlocal_edge_dofs * m_hho.get_mesh()->n_edges() - m_ndir_edge_dofs),
210  m_ntotal_dofs(m_ntotal_cell_dofs + m_ntotal_edge_dofs),
211  GlobRHS(Eigen::VectorXd::Zero(m_ntotal_edge_dofs)),
212  ScRHS(Eigen::VectorXd::Zero(m_ntotal_cell_dofs))
213  {
214  m_output << "[HHO_LocVarDiff] Initializing" << std::endl;
215  GlobMat.resize(m_ntotal_edge_dofs, m_ntotal_edge_dofs);
216  ScBeMat.resize(m_ntotal_cell_dofs, m_ntotal_edge_dofs);
217  // Do nothing
218  }
219 
221 
222  boost::timer::cpu_timer timer; // Time the matrix assembly
223  timer.start();
224  const Mesh* mesh = hho.get_mesh();
225 
226  //--------------- PREPARE SYSTEM ------------------------//
227 
228  // Global triplets for: system matrix, static condensaion/barycentric elimination
229  std::vector<Eigen::Triplet<double>> triplets_GlobMat;
230  std::vector<Eigen::Triplet<double>> triplets_ScBe;
231 
232  // Local bilinear form, triplets and source term (one per cell)
233  aT.resize(mesh->n_cells());
234  std::vector<std::vector<Eigen::Triplet<double>>> cell_triplets_GlobMat;
235  std::vector<std::vector<Eigen::Triplet<double>>> cell_triplets_ScBe;
236  std::vector<Eigen::VectorXd> cell_source(mesh->n_cells());
237  cell_triplets_GlobMat.resize(mesh->n_cells());
238  cell_triplets_ScBe.resize(mesh->n_cells());
239  size_t size_triplets_GlobMat = 0;
240  size_t size_triplets_ScBe = 0;
241 
242  //-------------- ASSEMBLE LOCAL CONTRIBUTIONS -------------//
243 
244  // Function to create local contribution between cell start and cell end-1
245  std::function<void(size_t, size_t)> construct_all_local_contributions
246  = [&](size_t start, size_t end)->void
247  {
248  for (size_t iT = start; iT < end; iT++) {
249  Cell* iCell = mesh->cell(iT);
250 
251  // Total number of edge degrees of freedom local to this cell (adjacent edges to the cell)
252  size_t nlocal_edges = iCell->n_edges();
253  size_t edge_dofs = nlocal_edges * m_nlocal_edge_dofs;
254 
255  // Local bilinear form and source term
256  size_t doeT = std::max( std::max(m_K,m_Ldeg) + m_K+1 , 2*m_K + _deg_kappa );
257  size_t doeF = 2*m_K + 1;
258  ElementQuad elquad(hho, iT, doeT, doeF);
259 
260  aT[iT] = diffusion_operator(hho, iT, elquad);
261  Eigen::VectorXd bT = load_operator(hho, iT, elquad);
262 
263  // Local matrix and right-hand side on the edge unknowns
264  Eigen::MatrixXd MatF = Eigen::MatrixXd::Zero(edge_dofs,edge_dofs);
265  cell_source[iT] = Eigen::VectorXd::Zero(edge_dofs);
266 
267  if (m_L>=0) {
268  // STATIC CONDENSATION OF ELEMENT UNKNOWNS
269 
270  // Perform static condensation
271  Eigen::MatrixXd ATT = aT[iT].topLeftCorner(m_nlocal_cell_dofs, m_nlocal_cell_dofs);
272  Eigen::MatrixXd ATF = aT[iT].topRightCorner(m_nlocal_cell_dofs, edge_dofs);
273  Eigen::MatrixXd AFF = aT[iT].bottomRightCorner(edge_dofs, edge_dofs);
274 
275  Eigen::PartialPivLU<Eigen::MatrixXd> invATT;
276  invATT.compute(ATT);
277 
278  Eigen::MatrixXd invATT_ATF = invATT.solve(ATF);
279  Eigen::VectorXd invATT_bTcell = invATT.solve(bT.head(m_nlocal_cell_dofs));
280  MatF = AFF - ATF.transpose() * invATT_ATF;
281 
282  cell_source[iT] = bT.tail(edge_dofs) - ATF.transpose() * invATT_bTcell;
283 
284  // Assemble local triplets for static condensation operator
285  ScRHS.segment(iT * m_nlocal_cell_dofs, m_nlocal_cell_dofs) = invATT_bTcell;
286  for (size_t i = 0; i < m_nlocal_cell_dofs; i++) {
287  for (size_t jlF = 0; jlF < nlocal_edges; jlF++) {
288  const size_t jF = iCell->edge(jlF)->global_index();
289  for (size_t jk = 0; jk < m_nlocal_edge_dofs; jk++) {
290  const size_t jLocal = jlF * m_nlocal_edge_dofs + jk;
291  const size_t jGlobal = jF * m_nlocal_edge_dofs + jk;
292  cell_triplets_ScBe[iT].emplace_back(iT * m_nlocal_cell_dofs + i, jGlobal, invATT_ATF(i, jLocal));
293  }
294  }
295  }
296  size_triplets_ScBe += cell_triplets_ScBe[iT].size();
297 
298  } else {
299  // BARYCENTRIC ELIMINATION OF ELEMENT UNKNOWNS
300  // Create reduction matrix: 1+nlocal_edges * nlocal_edges matrix with the coefficients on the first row, and the identity below. When multiplied by the edge unknowns, return cell and edge unknowns
301  // Note that the basis functions are constant, but not necessarily assumed to be one (which is not the case after orthonormalisation for example), which is why we have to adjust the first row.
302  Eigen::MatrixXd red_matT = Eigen::MatrixXd::Zero(1+nlocal_edges,nlocal_edges);
303  red_matT.row(0) = hho.compute_weights(iT);
304  VectorRd xT = iCell->center_mass();
305  double phiT_cst = hho.CellBasis(iT).function(0, xT);
306  for (size_t ilF = 0; ilF < nlocal_edges; ilF++){
307  VectorRd xF = iCell->edge(ilF)->center_mass();
308  size_t iF = iCell->edge(ilF)->global_index();
309  double phiF_cst = hho.EdgeBasis(iF).function(0, xF);
310  red_matT(0,ilF) *= phiF_cst / phiT_cst;
311  }
312  red_matT.bottomRightCorner(nlocal_edges,nlocal_edges) = Eigen::MatrixXd::Identity(nlocal_edges,nlocal_edges);
313 
314  cell_source[iT] = red_matT.transpose() * bT;
315  MatF = red_matT.transpose() * aT[iT] * red_matT;
316 
317  // Assemble local triplets for barycentric combination to recover cell unknown
318  for (size_t jlF = 0; jlF < nlocal_edges; jlF++) {
319  const size_t jF = iCell->edge(jlF)->global_index();
320  const size_t jGlobal = jF * m_nlocal_edge_dofs;
321  cell_triplets_ScBe[iT].emplace_back(iT, jGlobal, red_matT(0,jlF));
322  }
323  size_triplets_ScBe += cell_triplets_ScBe[iT].size();
324 
325  }
326 
327  // Assemble local triplets for scheme's matrix and source term
328  for (size_t ilF = 0; ilF < nlocal_edges; ilF++) {
329  const size_t iF = iCell->edge(ilF)->global_index();
330  for (size_t ik = 0; ik < m_nlocal_edge_dofs; ik++) {
331  const size_t iLocal = ilF * m_nlocal_edge_dofs + ik;
332  const size_t iGlobal = iF * m_nlocal_edge_dofs + ik;
333  for (size_t jlF = 0; jlF < nlocal_edges; jlF++) {
334  const size_t jF = iCell->edge(jlF)->global_index();
335  for (size_t jk = 0; jk < m_nlocal_edge_dofs; jk++) {
336  const size_t jLocal = jlF * m_nlocal_edge_dofs + jk;
337  const size_t jGlobal = jF * m_nlocal_edge_dofs + jk;
338  cell_triplets_GlobMat[iT].emplace_back(iGlobal, jGlobal, MatF(iLocal, jLocal));
339  }
340  }
341  }
342  }
343  size_triplets_GlobMat += cell_triplets_GlobMat[iT].size();
344 
345  }
346  }; // End function to construct local contributions
347 
348  // Running the local constructions in parallel
349  parallel_for(mesh->n_cells(), construct_all_local_contributions, m_use_threads);
350 
351  // Assemble local contribution into global matrix
352  triplets_ScBe.reserve(size_triplets_ScBe);
353  triplets_GlobMat.reserve(size_triplets_GlobMat);
354  for (size_t iT = 0; iT < mesh->n_cells(); iT++){
355  for (size_t i = 0; i < cell_triplets_ScBe[iT].size(); i++){
356  triplets_ScBe.push_back(cell_triplets_ScBe[iT][i]);
357  }
358  for (size_t i = 0; i < cell_triplets_GlobMat[iT].size(); i++){
359  triplets_GlobMat.push_back(cell_triplets_GlobMat[iT][i]);
360  }
361  Cell& T = *mesh->cell(iT);
362  for (size_t ilF = 0; ilF < T.n_edges(); ilF++) {
363  const size_t iF = T.edge(ilF)->global_index();
364  for (size_t ik = 0; ik < m_nlocal_edge_dofs; ik++) {
365  const size_t iLocal = ilF * m_nlocal_edge_dofs + ik;
366  const size_t iGlobal = iF * m_nlocal_edge_dofs + ik;
367  GlobRHS(iGlobal) += cell_source[iT](iLocal);
368  }
369  }
370  }
371 
372  if (m_BC.name()=="Neumann"){
373  // Neumann BC: remove a row in the matrix and fix the first degree of freedom
374  triplets_GlobMat.erase(std::remove_if(std::begin(triplets_GlobMat), std::end(triplets_GlobMat),
375  [](const auto& x) { return (x.row() == 0); }), std::end(triplets_GlobMat));
376  triplets_GlobMat.emplace_back(0, 0, 1);
377  GlobRHS(0) = 0;
378  }
379 
380  // Assemble the global linear system (without BC), and matrix to recover statically-condensed cell dofs
381  GlobMat.setFromTriplets(std::begin(triplets_GlobMat), std::end(triplets_GlobMat));
382  ScBeMat.setFromTriplets(std::begin(triplets_ScBe), std::end(triplets_ScBe));
383 
384  // Record assembly time
385  // _assembly_time = timer.elapsed().user + timer.elapsed().system;
386  _assembly_time = timer.elapsed().wall;
387 
388  }
389 
391  const Mesh* mesh = hho.get_mesh();
392  boost::timer::cpu_timer timer; // Time the matrix assembly
393  timer.start();
394 
395  //-------------- TREATMENT OF BOUNDARY CONDITIONS -------------//
396 
397  // If Dirichlet, the final system is only posed on the interior edge unknowns and we have to subtract from the source
398  // term the contribution of the boundary values
399  // If Neumann, the final system is posed on all edge unknowns
400 
401  size_t n_unknowns = 0;
402  size_t n_fixed_dofs = 0;
403  Eigen::VectorXd B;
404  Eigen::VectorXd UDir;
405 
406  if (m_BC.name() != "Neumann"){
407  // Dirichlet boundary conditions
408  n_unknowns = m_nnondir_edge_dofs;
409  n_fixed_dofs = m_ndir_edge_dofs;
410  SysMat = GlobMat.topLeftCorner(n_unknowns, n_unknowns);
411 
412  // Boundary value: UDir corresponds to the L2 projection of the exact solution on the polynomial spaces on the Dirichlet edges (last BC.n_dir_edges() edges)
413  UDir = Eigen::VectorXd::Zero(n_fixed_dofs);
414  size_t n_dir_edges = m_BC.n_dir_edges();
415  size_t n_nondir_edges = mesh->n_edges() - n_dir_edges;
416  for (size_t idF = 0; idF < n_dir_edges; idF++){
417  Edge* edge = mesh->edge(n_nondir_edges + idF);
418  size_t iF = edge->global_index();
419  QuadratureRule quadF = generate_quadrature_rule(*edge, 2*m_K+2);
420  boost::multi_array<double, 2> phiF_quadF = evaluate_quad<Function>::compute(hho.EdgeBasis(iF), quadF);
421  UDir.segment(idF * m_nlocal_edge_dofs, m_nlocal_edge_dofs) = l2_projection<HybridCore::PolyEdgeBasisType>(exact_solution, hho.EdgeBasis(iF), quadF, phiF_quadF);
422  }
423 
424  B = GlobRHS.segment(0, n_unknowns) - GlobMat.topRightCorner(n_unknowns, n_fixed_dofs) * UDir;
425 
426  } else {
427  // We will solve the complete system
428  n_unknowns = m_ntotal_edge_dofs;
429  SysMat = GlobMat;
430  B = GlobRHS;
431  UDir = Eigen::VectorXd::Zero(n_fixed_dofs);
432  }
433 
434  //-------------- SOLVE CONDENSED SYSTEM -------------//
435 
436  Eigen::VectorXd xF = Eigen::VectorXd::Zero(n_unknowns);
437 
438  // if (solver_type == "ma41") {
439  // Eigen::MA41<Eigen::SparseMatrix<double>, Eigen::VectorXd> solver;
440  // solver.analyzePattern(SysMat);
441  // solver.factorize(SysMat);
442  // xF = solver.solve(B);
443  // } else {
444  Eigen::BiCGSTAB<Eigen::SparseMatrix<double> > solver;
445  solver.compute(SysMat);
446  xF = solver.solve(B);
447  std::cout << " [solver] #iterations: " << solver.iterations() << ", estimated error: " << solver.error() << std::endl;
448  // }
449  _solving_error = (SysMat * xF - B).norm();
450  // Recover the fixed boundary values, cell unknowns (from static condensation/barycentric elimination)
451  Eigen::VectorXd Xh = Eigen::VectorXd::Zero(m_ntotal_dofs);
452  Xh.tail(n_fixed_dofs) = UDir;
453  Xh.segment(m_ntotal_cell_dofs, n_unknowns) = xF;
454  if (m_L>=0) {
455  Xh.head(m_ntotal_cell_dofs) = ScRHS - ScBeMat * Xh.tail(m_ntotal_edge_dofs);
456  } else {
457  Xh.head(m_ntotal_cell_dofs) = ScBeMat * Xh.tail(m_ntotal_edge_dofs);
458  }
459 
460  // Only Neumann: translate to get the proper average
461  if (m_BC.name()=="Neumann"){
462  // Compute average to translate
463  double average = 0.;
464  double total_measure = 0;
465  for (size_t iT = 0; iT < mesh->n_cells(); iT++){
466  Cell& T = *mesh->cell(iT);
467  total_measure += T.measure();
468  HybridCore::PolyCellBasisType basisT = hho.CellBasis(iT);
469  QuadratureRule quadT = generate_quadrature_rule(T, 2*m_K);
470  boost::multi_array<double, 2> phiT_quadT = evaluate_quad<Function>::compute(basisT, quadT);
471  for (size_t i = 0; i < basisT.dimension(); i++){
472  for (size_t iqn = 0; iqn < quadT.size(); iqn++){
473  average += quadT[iqn].w * Xh(iT * m_nlocal_cell_dofs + i) * basisT.function(i, quadT[iqn].vector());
474  }
475  }
476  }
477  double average_exact_sol = 0.0;
478  for (auto& T : mesh->get_cells()){
479  QuadratureRule quadT = generate_quadrature_rule(*T, 2 * m_Ldeg + 2);
480  for (QuadratureNode& qT : quadT){
481  average_exact_sol += qT.w * exact_solution(qT.vector());
482  }
483  }
484  average_exact_sol /= total_measure;
485 
486  // Translate the cells and edges
487  // We compute the interpolant of the constant function "average_exact_sol - average"
488  // and we translate Xh by that amount
489  std::function<double(VectorRd)> AveDiff = [&average_exact_sol,&average](VectorRd x)->double
490  { return average_exact_sol - average;};
491 
492  UVector Cst = hho.interpolate(AveDiff, m_L, m_K, 2*m_K+3);
493  Xh += Cst.asVectorXd();
494  }
495 
496  _solving_time = timer.elapsed().user + timer.elapsed().system; // Record the final solving time
497 
498  return UVector(Xh, *hho.get_mesh(), m_L, m_K);
499  }
500 
501  //********************************
502  // local diffusion matrix
503  //********************************
504 
505  Eigen::MatrixXd HHO_LocVarDiff::diffusion_operator(HybridCore &hho, const size_t iT, const ElementQuad &elquad) const
506  {
507 
508  boost::timer::cpu_timer timeint;
509 
510  const auto mesh = hho.get_mesh();
511  const size_t dimPKcell = DimPoly<Cell>(m_K);
512  const size_t dimPKcell_vec = mesh->dim() * dimPKcell;
513  Cell* cell = mesh->cell(iT);
514  const size_t nedgesT = cell->n_edges();
515 
516  // Total number of degrees of freedom local to this cell (cell and its adjacent edges)
517  size_t local_dofs = m_nlocal_cell_dofs + nedgesT * m_nlocal_edge_dofs;
518 
519  //------------------- Initialisatons: quadratures, mass matrices... --------------------//
520 
521  // Note: representing the gradient reconstruction GT supposes a basis for (P^k)^d. This basis can be
522  // built from the basis (phi_i)_i of P^k, by working component-by-component:
523  // (phi_1 e_1, ..., phi_N e_1, phi_1 e_2, ..., phi_N e_2, ...),
524  // where e_1=[1 0], e_2=[0 1]... is the canonical basis of R^d
525  // Hence, the first dimPKcell functions are on the first component of R^d, the next dimPKcell on the second
526  // component etc.
527  // We do not explicitly build this basis function, but we compute the values of these functions at the
528  // quadrature nodes, using the values computed for the scalar basis functions
529 
530 
531  // QUADRATURES
532  // Cell quadrature nodes, and values of cell basis functions (up to degree K+1) and gradients thereof.
533  QuadratureRule quadT = elquad.get_quadT();
534  boost::multi_array<double, 2> phiT_quadT = elquad.get_phiT_quadT();
535  boost::multi_array<VectorRd, 2> dphiT_quadT = elquad.get_dphiT_quadT();
536 
537  // Vector basis functions (up to degree K) at the quadrature nodes.
538  boost::multi_array<VectorRd, 2> vec_phiT_quadT = elquad.get_vec_phiT_quadT(m_K);
539 
540  // Diffusion tensor at the quadrature nodes
541  std::vector<MatrixRd> kappaT_quadT(quadT.size());
542  std::transform(quadT.begin(), quadT.end(), kappaT_quadT.begin(),
543  [this,&cell](QuadratureNode qr) -> Eigen::MatrixXd { return kappa(qr.vector(), cell); });
544 
545  // MASS MATRICES
546  // Scalar cell mass matrix (phi_i,phi_j)_T up to degree max(L,K) * (K+1), and
547  // Vector cell mass matrix (Phi_i,Phi_j)_T up to degree K*K [this one is block diagonal]
548  size_t maxdimPKL = std::max(m_nlocal_cell_dofs, dimPKcell);
549 
550  _itime[0] += timeint.elapsed().user + timeint.elapsed().system;
551  timeint.start();
552 
553  Eigen::MatrixXd MTT = compute_gram_matrix(phiT_quadT, phiT_quadT, quadT, maxdimPKL, m_nhighorder_dofs, "sym");
554  Eigen::MatrixXd VecMTT = Eigen::MatrixXd::Zero(dimPKcell_vec, dimPKcell_vec);
555  for (size_t r=0; r < mesh->dim(); r++){
556  VecMTT.block(r*dimPKcell, r*dimPKcell, dimPKcell, dimPKcell) = MTT.topLeftCorner(dimPKcell, dimPKcell);
557  }
558 
559  // Edge mass matrices:
560  // MFF[ilF]: edge-edge mass on edge with local number ilF, up to degree K*K
561  // MFT[ilF]: edge-cell mass on edge with local number ilF, up to degree K*(K+1)
562  // MTT_on_F[ilF]: cell-cell mass on edge with local number ilF, up to degree K*L
563  std::vector<Eigen::MatrixXd> MFF(nedgesT, Eigen::MatrixXd::Zero(m_nlocal_edge_dofs, m_nlocal_edge_dofs));
564  std::vector<Eigen::MatrixXd> MFT(nedgesT, Eigen::MatrixXd::Zero(m_nlocal_edge_dofs, m_nhighorder_dofs));
565  std::vector<Eigen::MatrixXd> MTT_on_F(nedgesT, Eigen::MatrixXd::Zero(dimPKcell, m_nlocal_cell_dofs));
566 
567  for (size_t ilF = 0; ilF < nedgesT; ilF++) {
568 
569  // Edge quadrature nodes and values of cell and edge basis functions (and gradients) at these nodes
570  auto quadF = elquad.get_quadF(ilF);
571  boost::multi_array<double, 2> phiT_quadF = elquad.get_phiT_quadF(ilF);
572  boost::multi_array<double, 2> phiF_quadF = elquad.get_phiF_quadF(ilF);
573 
574  // Mass matrices
575  MFF[ilF] = compute_gram_matrix(phiF_quadF, phiF_quadF, quadF, "sym");
576  MFT[ilF] = compute_gram_matrix(phiF_quadF, phiT_quadF, quadF, "nonsym");
577  MTT_on_F[ilF] = compute_gram_matrix(phiT_quadF, phiT_quadF, quadF, dimPKcell, m_nlocal_cell_dofs, "nonsym");
578  }
579 
580  _itime[1] += timeint.elapsed().user + timeint.elapsed().system;
581  timeint.start();
582 
583  // STIFNESS mass-matrices (and the like): (\nabla phi_i,\nabla phi_j)_T up to degree (K+1)*(K+1)
584  // and (\nabla phi_i, Phi_j)_T up to degree (K+1)*K
585  Eigen::MatrixXd StiffT = compute_gram_matrix(dphiT_quadT, dphiT_quadT, quadT, "sym");
586  Eigen::MatrixXd MdphiT_PhiT = compute_gram_matrix(dphiT_quadT, vec_phiT_quadT, quadT, "nonsym");
587 
588  _itime[2] += timeint.elapsed().user + timeint.elapsed().system;
589  timeint.start();
590 
591  //-------------------- Compute GT, matrix of full gradient reconstruction ---------//
592 
593  // Right-hand side, starting with volumetric term (Phi_i, \nabla phi_j)_T
594  Eigen::MatrixXd RHS_GT = Eigen::MatrixXd::Zero(dimPKcell_vec, local_dofs);
595  RHS_GT.topLeftCorner(dimPKcell_vec, m_nlocal_cell_dofs) = (MdphiT_PhiT.topLeftCorner(m_nlocal_cell_dofs, dimPKcell_vec)).transpose();
596 
597  // Boundary terms
598  for (size_t r=0; r < mesh->dim(); r++){
599  for (size_t ilF = 0; ilF < nedgesT; ilF++) {
600  // Offset for edge unknowns
601  const size_t offset_F = m_nlocal_cell_dofs + ilF * m_nlocal_edge_dofs;
602  const auto& nTF = cell->edge_normal(ilF);
603 
604  // Contribution of cell unknowns, and then edge unknowns on F
605  RHS_GT.block(r*dimPKcell, 0, dimPKcell, m_nlocal_cell_dofs) -= nTF(r) * MTT_on_F[ilF];
606  RHS_GT.block(r*dimPKcell, offset_F, dimPKcell, m_nlocal_edge_dofs) +=
607  nTF(r) * (MFT[ilF].topLeftCorner(m_nlocal_edge_dofs, dimPKcell)).transpose();
608  }
609  }
610 
611  // Compute GT
612  Eigen::MatrixXd GT = (VecMTT.ldlt()).solve(RHS_GT);
613 
614  //------------- Consistent contribution (K GT, GT)_T to the local bilinear form ------//
615 
616 
617  // Weighted mass matrix (K Phi_i, Phi_j)_T of the basis (Phi_i)_i of (P^k)^d
618  // We start by creating K Phi_i at the quadrature nodes
619  boost::multi_array<VectorRd, 2> kappa_vec_phiT_quadT;
620  kappa_vec_phiT_quadT.resize( boost::extents[dimPKcell_vec][quadT.size()] );
621  for (size_t i = 0; i < dimPKcell_vec; i++){
622  for (size_t iqn = 0; iqn < quadT.size(); iqn++){
623  kappa_vec_phiT_quadT[i][iqn] = kappaT_quadT[iqn] * vec_phiT_quadT[i][iqn];
624  }
625  }
626  _itime[3] += timeint.elapsed().user + timeint.elapsed().system;
627  timeint.start();
628 
629  Eigen::MatrixXd kappaVecMTT = compute_gram_matrix(kappa_vec_phiT_quadT, vec_phiT_quadT, quadT, vec_phiT_quadT.shape()[0], vec_phiT_quadT.shape()[0], "sym");
630 
631  _itime[4] += timeint.elapsed().user + timeint.elapsed().system;
632  timeint.start();
633 
634  Eigen::MatrixXd ATF = GT.transpose() * kappaVecMTT * GT;
635 
636 
637  //------------- Compute PT, matrix of potential reconstruction, using GT ------//
638 
639  // We write that \nabla pT = projection on \nabla P^{k+1} of GT, and add the closure relation:
640  // (nabla pT v, nabla w)_T + lambda_T(p_T v,1)_T(w,1)_T = (GT v,\nabla w)_T + lambda_T(v_T,1)_T(w,1)_T
641 
642  // Right-hand side, starting with volumetric term
643  Eigen::MatrixXd RHS_PT = MdphiT_PhiT * GT;
644 
645  // Vector LT of (phi_j,1)_T for phi_j up to degree K+1, and LT^t*LT, for the closure relation
646  Eigen::VectorXd LT = (MTT.row(0)).transpose();
647  Eigen::MatrixXd LTtLT = LT * (LT.transpose());
648  double scalT = StiffT.trace() / std::pow(LT.norm(), 2);
649 
650  // Add closure relation and compute PT
651  RHS_PT.topLeftCorner(m_nhighorder_dofs, m_nlocal_cell_dofs) +=
652  scalT * LTtLT.topLeftCorner(m_nhighorder_dofs, m_nlocal_cell_dofs);
653  Eigen::MatrixXd PT = ((StiffT + scalT*LTtLT).ldlt()).solve(RHS_PT);
654 
655 
656  //-------------------- Compute stabilisation term sT ---------//
657 
658  Eigen::MatrixXd STF = Eigen::MatrixXd::Zero(local_dofs, local_dofs);
659 
660  // Cell residual delta_T^l = pi_T^l (rT uT) - u_T
661 
662  Eigen::MatrixXd MTT_LKp1 = MTT.topLeftCorner(m_nlocal_cell_dofs, m_nhighorder_dofs);
663  Eigen::MatrixXd MTT_LL = MTT.topLeftCorner(m_nlocal_cell_dofs, m_nlocal_cell_dofs);
664  Eigen::MatrixXd deltaTL = MTT_LL.ldlt().solve( MTT_LKp1 * PT );
665  deltaTL.topLeftCorner(m_nlocal_cell_dofs, m_nlocal_cell_dofs) -= Eigen::MatrixXd::Identity(m_nlocal_cell_dofs, m_nlocal_cell_dofs);
666 
667  for (size_t ilF = 0; ilF < nedgesT; ilF++) {
668  // Two options for stabilisation: diameter of edge, or ratio measure cell/measure edge
669  double dTF = cell->edge(ilF)->diam();
670  // double dTF = cell->measure() / cell->edge(ilF)->measure();
671 
672  VectorRd xF = cell->edge(ilF)->center_mass();
673 
674  // auto kappa_TF = kappa(xF, cell).trace();
675 
676  const VectorRd &nTF = cell->edge_normal(ilF);
677  const double kappa_TF = (kappa(xF, cell) * nTF).dot(nTF);
678 
679  // Edge residual delta_TF^k = pi_F^k (rT uT) - u_F
680  Eigen::MatrixXd MFFinv = MFF[ilF].inverse();
681  Eigen::MatrixXd deltaTFK = MFFinv * MFT[ilF] * PT;
682  deltaTFK.block(0, m_nlocal_cell_dofs + ilF * m_nlocal_edge_dofs, m_nlocal_edge_dofs, m_nlocal_edge_dofs) -=
683  Eigen::MatrixXd::Identity(m_nlocal_edge_dofs, m_nlocal_edge_dofs);
684 
685  // Stabilisation term: here, we actually project deltaTL on P^k(F) so, for l=k+1, it actually corresponds to the stabilisation used in HDG methods (see Section 5.1.6 of HHO book)
686  Eigen::MatrixXd deltaTFK_minus_deltaTL = deltaTFK - MFFinv * MFT[ilF].topLeftCorner(m_nlocal_edge_dofs, m_nlocal_cell_dofs) * deltaTL;
687 
688  STF += (kappa_TF / dTF) * deltaTFK_minus_deltaTL.transpose() * MFF[ilF] * deltaTFK_minus_deltaTL;
689  }
690 
691  _itime[5] += timeint.elapsed().user + timeint.elapsed().system;
692 
693  // Adjust local bilinear form with stabilisation term
694  ATF += mesh->dim() * STF;
695 
696  return ATF;
697 
698  }
699 
700 
701  //********************************
702  // local load term
703  //********************************
704 
705  Eigen::VectorXd HHO_LocVarDiff::load_operator(HybridCore &hho, const size_t iT, const ElementQuad &elquad) const {
706  // Load for the cell DOFs (first indices) and edge DOFs (last indices)
707  const auto mesh = hho.get_mesh();
708  Cell* cell = mesh->cell(iT);
709  size_t cell_edge_dofs = m_nlocal_cell_dofs + cell->n_edges()*m_nlocal_edge_dofs;
710  Eigen::VectorXd b = Eigen::VectorXd::Zero(cell_edge_dofs);
711 
712  // Quadrature nodes and values of cell basis functions at these nodes
713  auto quadT = elquad.get_quadT();
714  size_t nbq = quadT.size();
715  boost::multi_array<double, 2> phiT_quadT = elquad.get_phiT_quadT();
716 
717  // Value of source times quadrature weights at the quadrature nodes
718  Eigen::ArrayXd weight_source_quad = Eigen::ArrayXd::Zero(nbq);
719  for (size_t iqn = 0; iqn < nbq; iqn++){
720  weight_source_quad(iqn) = quadT[iqn].w * source(quadT[iqn].vector(), cell);
721  }
722 
723  for (size_t i=0; i < m_nlocal_cell_dofs; i++){
724  for (size_t iqn = 0; iqn < quadT.size(); iqn++){
725  b(i) += weight_source_quad[iqn] * phiT_quadT[i][iqn];
726  }
727  }
728 
729  // Boundary values, if we have a boundary cell
730  if (cell->is_boundary()){
731  // Boundary values only on boundary Neumann edges
732  for (size_t ilF = 0; ilF < cell->n_edges(); ilF++) {
733  Edge* F = cell->edge(ilF);
734  if (m_BC.type(*F)=="neu"){
735  const size_t iF = F->global_index();
736  // BC on boundary edges
737  if (F->is_boundary()){
738  // Offset for edge unknowns
739  const size_t offset_F = m_nlocal_cell_dofs + ilF * m_nlocal_edge_dofs;
740  // Normal to the edge and bases function
741  const auto& nTF = cell->edge_normal(ilF);
742  const auto& basisF = hho.EdgeBasis(iF);
743  // for each DOF of the boundary edge
744  for (size_t i = 0; i < m_nlocal_edge_dofs; i++){
745  QuadratureRule quadF = generate_quadrature_rule(*F, 2*m_K+2);
746  std::function<double(VectorRd)> Kgrad_n = [&](VectorRd p){
747  return nTF.dot(kappa(p,cell) * grad_exact_solution(p,cell)) * basisF.function(i,p);
748  };
749  for (QuadratureNode& qF : quadF){
750  b(offset_F + i) += qF.w * Kgrad_n(qF.vector());
751  }
752  }
753  }
754  }
755  }
756  }
757 
758  return b;
759  }
760 
762  const auto mesh = hho.get_mesh();
763  double value = 0.0;
764 
765  for (size_t iT = 0; iT < mesh->n_cells(); iT++) {
766  Eigen::VectorXd XTF = Xh.restr(iT);
767  value += XTF.transpose() * aT[iT] * XTF;
768  }
769 
770  return sqrt(value);
771  }
772 
773 
775 } // end of namespace HArDCore2D
776 
777 #endif //_HHO_LOCVARDIFF_HPP
std::function< T(const VectorRd &, const Cell *)> CellFType
type for function of a point, that could be discontinuous between cells. T is the type of value of th...
Definition: TestCase.hpp:42
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 std::string name() const
Returns the complete name of the boundary condition.
Definition: BoundaryConditions.hpp:80
const size_t n_dir_edges() const
Returns the number of Dirichlet edges.
Definition: BoundaryConditions.hpp:70
Definition: elementquad.hpp:49
Definition: basis.hpp:311
The HHO_LocVarDiff class provides tools to implement the HHO method for the diffusion problem.
Definition: HHO_LocVarDiff.hpp:65
Definition: hybridcore.hpp:179
Definition: hybridcore.hpp:82
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
FunctionValue function(size_t i, const VectorRd &x) const
Evaluate the i-th function at point x.
Definition: basis.hpp:351
Eigen::Vector2d VectorRd
Definition: basis.hpp:55
size_t dimension() const
Dimension of the family. This is actually the number of functions in the family, not necessarily line...
Definition: basis.hpp:345
Eigen::MatrixXd compute_gram_matrix(const boost::multi_array< VectorRd, 2 > &B1, const boost::multi_array< double, 2 > &B2, const QuadratureRule &qr)
Compute the Gram-like matrix given a family of vector-valued and one of scalar-valued functions by te...
Definition: basis.cpp:225
static boost::multi_array< typename detail::basis_evaluation_traits< BasisType, BasisFunction >::ReturnValue, 2 > compute(const BasisType &basis, const QuadratureRule &quad)
Generic basis evaluation.
Definition: basis.hpp:2288
std::function< T(const VectorRd &)> FType
type for function of point. T is the type of value of the function
Definition: basis.hpp:62
static void parallel_for(unsigned nb_elements, std::function< void(size_t start, size_t end)> functor, bool use_threads=true, unsigned nb_threads_max=1e9)
Generic function to execute threaded processes.
Definition: parallel_for.hpp:42
bool use_threads
Definition: HHO_DiffAdvecReac.hpp:47
size_t L
Definition: HHO_DiffAdvecReac.hpp:46
size_t K
Definition: HHO_DiffAdvecReac.hpp:46
double EnergyNorm(HybridCore &hho, const UVector Xh)
Discrete energy norm (associated to the diffusion operator) of an hybrid function.
Definition: HHO_LocVarDiff.hpp:761
const size_t get_nhighorder_dofs()
Number of DOFs per cell for high-order (K+1) polynomials.
Definition: HHO_LocVarDiff.hpp:123
const size_t get_ntotal_dofs()
Total number of degrees of freedom over the entire mesh.
Definition: HHO_LocVarDiff.hpp:131
HHO_LocVarDiff(HybridCore &hho, size_t K, int L, CellFType< MatrixRd > kappa, size_t deg_kappa, CellFType< double > source, BoundaryConditions BC, FType< double > exact_solution, CellFType< VectorRd > grad_exact_solution, std::string solver_type, bool use_threads, std::ostream &output=std::cout)
Constructor of the class.
Definition: HHO_LocVarDiff.hpp:189
const size_t get_nlocal_cell_dofs()
Number of DOFs in each cell.
Definition: HHO_LocVarDiff.hpp:119
void assemble(HybridCore &hho)
Assemble and solve the scheme.
Definition: HHO_LocVarDiff.hpp:220
double get_itime(size_t idx) const
various intermediate assembly times
Definition: HHO_LocVarDiff.hpp:113
double get_solving_error() const
residual after solving the scheme
Definition: HHO_LocVarDiff.hpp:108
double get_assembly_time() const
cpu time to assemble the scheme
Definition: HHO_LocVarDiff.hpp:98
UVector solve(HybridCore &hho)
Definition: HHO_LocVarDiff.hpp:390
const size_t get_nlocal_edge_dofs()
Number of DOFs on each edge.
Definition: HHO_LocVarDiff.hpp:121
const size_t get_ntotal_cell_dofs()
Total number of cell DOFs over the entire mesh.
Definition: HHO_LocVarDiff.hpp:125
const size_t get_ntotal_edge_dofs()
Total number of edge DOFs over the entire mesh.
Definition: HHO_LocVarDiff.hpp:127
double get_solving_time() const
cpu time to solve the scheme
Definition: HHO_LocVarDiff.hpp:103
const size_t get_ndir_edge_dofs()
Total number of edge DOFs for Dirichlet edges.
Definition: HHO_LocVarDiff.hpp:129
Eigen::SparseMatrix< double > get_SysMat()
Return the (statically condensed) matrix system.
Definition: HHO_LocVarDiff.hpp:93
const boost::multi_array< double, 2 > get_phiF_quadF(size_t ilF) const
Returns values of edge basis functions at edge quadrature nodes, for edge with local number ilF.
Definition: elementquad.hpp:91
Eigen::VectorXd compute_weights(size_t iT) const
Computes the weights to get cell values from edge values when l=-1.
Definition: hybridcore.cpp:138
const QuadratureRule & get_quadF(size_t ilF) const
Returns quadrature rules on edge with local number ilF.
Definition: elementquad.hpp:67
const QuadratureRule & get_quadT() const
Returns quadrature rules in cell.
Definition: elementquad.hpp:61
Eigen::VectorXd restr(size_t iT) const
Extract the restriction of the unknowns corresponding to cell iT and its edges.
Definition: hybridcore.cpp:29
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
Eigen::VectorXd & asVectorXd() const
Return the values as an Eigen vector.
Definition: hybridcore.hpp:92
boost::multi_array< VectorRd, 2 > get_vec_phiT_quadT(size_t degree) const
Builds on the fly the values of vector cell basis functions at cell quadrature nodes....
Definition: elementquad.cpp:62
const boost::multi_array< VectorRd, 2 > & get_dphiT_quadT() const
Returns values of gradients of cell basis functions at cell quadrature nodes.
Definition: elementquad.hpp:79
UVector interpolate(const ContinuousFunction &f, const int deg_cell, const size_t deg_edge, size_t doe) const
Compute the interpolant in the discrete space of a continuous function.
Definition: hybridcore.hpp:290
const boost::multi_array< double, 2 > & get_phiT_quadT() const
Returns values of cell basis functions at cell quadrature nodes.
Definition: elementquad.hpp:73
const Mesh * get_mesh() const
Returns a pointer to the mesh.
Definition: hybridcore.hpp:201
const PolyEdgeBasisType & EdgeBasis(size_t iE) const
Return edge basis for edge with global index iE.
Definition: hybridcore.hpp:218
size_t const DimPoly(int m)
const boost::multi_array< double, 2 > & get_phiT_quadF(size_t ilF) const
Returns values of cell basis functions at edge quadrature nodes, for edge with local number ilF.
Definition: elementquad.hpp:85
const PolyCellBasisType & CellBasis(size_t iT) const
Return cell basis for element with global index iT.
Definition: hybridcore.hpp:210
std::vector< Cell * > get_cells() const
lists the cells in the mesh.
Definition: Mesh2D.hpp:78
Polytope< DIMENSION > Cell
Definition: Polytope2D.hpp:151
size_t n_edges() const
Return the number of edges of the Polytope.
Definition: Polytope2D.hpp:88
Cell * cell(std::size_t index) const
get a constant pointer to a cell using its global index
Definition: Mesh2D.hpp:178
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
size_t global_index() const
Return the global index of the Polytope.
Definition: Polytope2D.hpp:73
std::size_t n_edges() const
number of edges in the mesh.
Definition: Mesh2D.hpp:61
Edge * edge(std::size_t index) const
get a constant pointer to a edge using its global index
Definition: Mesh2D.hpp:168
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
if(strcmp(field, 'real')) % real valued entries T
Definition: mmread.m:93
Definition: PastixInterface.hpp:16
Definition: ddr-klplate.hpp:27
Description of one node and one weight from a quadrature rule.
Definition: quadraturerule.hpp:41