HArD::Core3D
Hybrid Arbitrary Degree::Core 3D - Library to implement 3D schemes with vertex, edge, face and cell polynomials as unknowns
HHO_Diffusion.hpp
Go to the documentation of this file.
1 // Implementation of the HHO scheme in 3D for the diffusion equation, with K piecewise constant
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_DIFFUSION_HPP
28 #define _HHO_DIFFUSION_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 <hybridcore.hpp>
42 #include <elementquad.hpp>
43 #include <parallel_for.hpp>
44 #include <TestCase/TestCase.hpp>
46 
52 namespace HArDCore3D {
53 
58  // ----------------------------------------------------------------------------
59  // Class definition
60  // ----------------------------------------------------------------------------
61 
63 
64  class HHO_Diffusion {
65 
66  public:
69  HybridCore& hho,
70  size_t K,
71  int L,
72  CellFType<MatrixRd> kappa,
73  CellFType<double> source,
75  FType<double> exact_solution,
76  CellFType<VectorRd> grad_exact_solution,
77  std::string solver_type,
78  bool use_threads,
79  std::ostream & output = std::cout
80  );
81 
83  void assemble(HybridCore& hho);
84  UVector solve(HybridCore& hho);
85 
87  double EnergyNorm(HybridCore& hho, const UVector Xh);
88 
90  inline double get_assembly_time() const
91  {
92  return double(_assembly_time) * pow(10, -9);
93  };
95  inline double get_solving_time() const
96  {
97  return double(_solving_time) * pow(10, -9);
98  };
100  inline double get_solving_error() const
101  {
102  return _solving_error;
103  };
105  inline double get_itime(size_t idx) const
106  {
107  return double(_itime[idx]) * pow(10, -9);
108  };
109 
111  const inline size_t get_nlocal_cell_dofs() { return m_nlocal_cell_dofs; }
113  const inline size_t get_nlocal_face_dofs() { return m_nlocal_face_dofs; }
115  const inline size_t get_nhighorder_dofs() { return m_nhighorder_dofs; }
117  const inline size_t get_ntotal_cell_dofs() { return m_ntotal_cell_dofs; }
119  const inline size_t get_ntotal_face_dofs() { return m_ntotal_face_dofs; }
121  const inline size_t get_ndir_face_dofs() { return m_ndir_face_dofs; }
123  const inline size_t get_ntotal_dofs() { return m_ntotal_dofs; }
124 
125  private:
127  Eigen::MatrixXd diffusion_operator(HybridCore& hho, const size_t iT, const ElementQuad& elquad) const;
128 
130  Eigen::VectorXd load_operator(HybridCore& hho, const size_t iT, const ElementQuad &elquad) const;
131 
132  // Reference to the HybridCore structure
133  HybridCore& m_hho;
134 
135  // Degrees on faces and in cells
136  size_t m_K;
137  int m_L;
138  size_t m_Ldeg;
139 
140  // Data
141  const CellFType<MatrixRd> kappa;
142  const CellFType<double> source;
143  const BoundaryConditions m_BC;
144  const FType<double> exact_solution;
145  const CellFType<VectorRd> grad_exact_solution;
146  const std::string solver_type;
147  const bool m_use_threads;
148  std::ostream & m_output;
149 
150  // DOFs
151  const size_t m_nlocal_cell_dofs;
152  const size_t m_nlocal_face_dofs;
153  const size_t m_nhighorder_dofs;
154  const size_t m_ntotal_cell_dofs;
155  const size_t m_ntotal_face_dofs;
156  const size_t m_ndir_face_dofs;
157  const size_t m_nnondir_face_dofs;
158  const size_t m_ntotal_dofs;
159 
160  // Local bilinear forms
161  std::vector<Eigen::MatrixXd> aT;
162  // Global system matrix
163  Eigen::SparseMatrix<double> GlobMat;
164  // If static condensation (L>=0): matrix to recover cell unknowns
165  // If barycentric elimination (L=-1): matrix to recover cell unknowns
166  Eigen::SparseMatrix<double> ScBeMat;
167  // Source terms for the system, and for recovering cell unknowns from static condensation
168  Eigen::VectorXd GlobRHS;
169  Eigen::VectorXd ScRHS;
170 
171  // Computation statistics
172  size_t _assembly_time;
173  size_t _solving_time;
174  double _solving_error;
175  mutable std::vector<size_t> _itime = std::vector<size_t>(10, 0);
176 
177  };
178 
179  HHO_Diffusion::HHO_Diffusion(HybridCore& hho, size_t K, int L, CellFType<MatrixRd> 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)
180  : m_hho(hho),
181  m_K(K),
182  m_L(L),
183  m_Ldeg(std::max(L,0)),
184  kappa(kappa),
185  source(source),
186  m_BC(BC),
187  exact_solution(exact_solution),
188  grad_exact_solution(grad_exact_solution),
189  solver_type(solver_type),
190  m_use_threads(use_threads),
191  m_output(output),
192  m_nlocal_cell_dofs(DimPoly<Cell>(m_Ldeg)),
193  m_nlocal_face_dofs(DimPoly<Face>(m_K)),
194  m_nhighorder_dofs(DimPoly<Cell>(m_K+1)),
195  m_ntotal_cell_dofs(m_nlocal_cell_dofs * m_hho.get_mesh()->n_cells()),
196  m_ntotal_face_dofs(m_nlocal_face_dofs * m_hho.get_mesh()->n_faces()),
197  m_ndir_face_dofs(m_nlocal_face_dofs * m_BC.n_dir_faces()),
198  m_nnondir_face_dofs(m_nlocal_face_dofs * m_hho.get_mesh()->n_faces() - m_ndir_face_dofs),
199  m_ntotal_dofs(m_ntotal_cell_dofs + m_ntotal_face_dofs),
200  GlobRHS(Eigen::VectorXd::Zero(m_ntotal_face_dofs)),
201  ScRHS(Eigen::VectorXd::Zero(m_ntotal_cell_dofs))
202  {
203  m_output << "[HHO_Diffusion] Initializing" << std::endl;
204  GlobMat.resize(m_ntotal_face_dofs, m_ntotal_face_dofs);
205  ScBeMat.resize(m_ntotal_cell_dofs, m_ntotal_face_dofs);
206  // Do nothing
207  }
208 
210 
211  boost::timer::cpu_timer timer; // Time the matrix assembly
212  timer.start();
213  const Mesh* mesh = hho.get_mesh();
214 
215  //--------------- PREPARE SYSTEM ------------------------//
216 
217  // Global triplets for: system matrix, static condensaion/barycentric elimination
218  std::vector<Eigen::Triplet<double>> triplets_GlobMat;
219  std::vector<Eigen::Triplet<double>> triplets_ScBe;
220 
221  // Local bilinear form, triplets and source term (one per cell)
222  aT.resize(mesh->n_cells());
223  std::vector<std::vector<Eigen::Triplet<double>>> cell_triplets_GlobMat;
224  std::vector<std::vector<Eigen::Triplet<double>>> cell_triplets_ScBe;
225  std::vector<Eigen::VectorXd> cell_source(mesh->n_cells());
226  cell_triplets_GlobMat.resize(mesh->n_cells());
227  cell_triplets_ScBe.resize(mesh->n_cells());
228  size_t size_triplets_GlobMat = 0;
229  size_t size_triplets_ScBe = 0;
230 
231  //-------------- ASSEMBLE LOCAL CONTRIBUTIONS -------------//
232 
233  // Function to create local contribution between cell start and cell end-1
234  std::function<void(size_t, size_t)> construct_all_local_contributions
235  = [&](size_t start, size_t end)->void
236  {
237  for (size_t iT = start; iT < end; iT++) {
238  Cell* iCell = mesh->cell(iT);
239 
240  // Total number of face degrees of freedom local to this cell (adjacent faces to the cell)
241  size_t nlocal_faces = iCell->n_faces();
242  size_t face_dofs = nlocal_faces * m_nlocal_face_dofs;
243 
244  // Local bilinear form and source term
245  size_t doeT = m_Ldeg + m_K + 1;
246  size_t doeF = 2*m_K + 1;
247  ElementQuad elquad(hho, iT, doeT, doeF);
248 
249  aT[iT] = diffusion_operator(hho, iT, elquad);
250  Eigen::VectorXd bT = load_operator(hho, iT, elquad);
251 
252  // Local matrix and right-hand side on the face unknowns
253  Eigen::MatrixXd MatF = Eigen::MatrixXd::Zero(face_dofs,face_dofs);
254  cell_source[iT] = Eigen::VectorXd::Zero(face_dofs);
255 
256  if (m_L>=0) {
257  // STATIC CONDENSATION OF ELEMENT UNKNOWNS
258 
259  // Perform static condensation
260  Eigen::MatrixXd ATT = aT[iT].topLeftCorner(m_nlocal_cell_dofs, m_nlocal_cell_dofs);
261  Eigen::MatrixXd ATF = aT[iT].topRightCorner(m_nlocal_cell_dofs, face_dofs);
262  Eigen::MatrixXd AFF = aT[iT].bottomRightCorner(face_dofs, face_dofs);
263 
264  Eigen::PartialPivLU<Eigen::MatrixXd> invATT;
265  invATT.compute(ATT);
266 
267  Eigen::MatrixXd invATT_ATF = invATT.solve(ATF);
268  Eigen::VectorXd invATT_bTcell = invATT.solve(bT.head(m_nlocal_cell_dofs));
269  MatF = AFF - ATF.transpose() * invATT_ATF;
270 
271  cell_source[iT] = bT.tail(face_dofs) - ATF.transpose() * invATT_bTcell;
272 
273  // Assemble local triplets for static condensation operator
274  ScRHS.segment(iT * m_nlocal_cell_dofs, m_nlocal_cell_dofs) = invATT_bTcell;
275  for (size_t i = 0; i < m_nlocal_cell_dofs; i++) {
276  for (size_t jlF = 0; jlF < nlocal_faces; jlF++) {
277  const size_t jF = iCell->face(jlF)->global_index();
278  for (size_t jk = 0; jk < m_nlocal_face_dofs; jk++) {
279  const size_t jLocal = jlF * m_nlocal_face_dofs + jk;
280  const size_t jGlobal = jF * m_nlocal_face_dofs + jk;
281  cell_triplets_ScBe[iT].emplace_back(iT * m_nlocal_cell_dofs + i, jGlobal, invATT_ATF(i, jLocal));
282  }
283  }
284  }
285  size_triplets_ScBe += cell_triplets_ScBe[iT].size();
286 
287  } else {
288  // BARYCENTRIC ELIMINATION OF ELEMENT UNKNOWNS
289  // Create reduction matrix: 1+nlocal_faces * nlocal_faces matrix with the coefficients on the first row, and the identity below. When multiplied by the face unknowns, return cell and face unknowns
290  // 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.
291  Eigen::MatrixXd red_matT = Eigen::MatrixXd::Zero(1+nlocal_faces,nlocal_faces);
292  red_matT.row(0) = hho.compute_weights(iT);
293  VectorRd xT = iCell->center_mass();
294  double phiT_cst = hho.CellBasis(iT).function(0, xT);
295  for (size_t ilF = 0; ilF < nlocal_faces; ilF++){
296  VectorRd xF = iCell->face(ilF)->center_mass();
297  size_t iF = iCell->face(ilF)->global_index();
298  double phiF_cst = hho.FaceBasis(iF).function(0, xF);
299  red_matT(0,ilF) *= phiF_cst / phiT_cst;
300  }
301  red_matT.bottomRightCorner(nlocal_faces,nlocal_faces) = Eigen::MatrixXd::Identity(nlocal_faces,nlocal_faces);
302 
303  cell_source[iT] = red_matT.transpose() * bT;
304  MatF = red_matT.transpose() * aT[iT] * red_matT;
305 
306  // Assemble local triplets for barycentric combination to recover cell unknown
307  for (size_t jlF = 0; jlF < nlocal_faces; jlF++) {
308  const size_t jF = iCell->face(jlF)->global_index();
309  const size_t jGlobal = jF * m_nlocal_face_dofs;
310  cell_triplets_ScBe[iT].emplace_back(iT, jGlobal, red_matT(0,jlF));
311  }
312  size_triplets_ScBe += cell_triplets_ScBe[iT].size();
313 
314  }
315 
316  // Assemble local triplets for scheme's matrix and source term
317  for (size_t ilF = 0; ilF < nlocal_faces; ilF++) {
318  const size_t iF = iCell->face(ilF)->global_index();
319  for (size_t ik = 0; ik < m_nlocal_face_dofs; ik++) {
320  const size_t iLocal = ilF * m_nlocal_face_dofs + ik;
321  const size_t iGlobal = iF * m_nlocal_face_dofs + ik;
322  for (size_t jlF = 0; jlF < nlocal_faces; jlF++) {
323  const size_t jF = iCell->face(jlF)->global_index();
324  for (size_t jk = 0; jk < m_nlocal_face_dofs; jk++) {
325  const size_t jLocal = jlF * m_nlocal_face_dofs + jk;
326  const size_t jGlobal = jF * m_nlocal_face_dofs + jk;
327  cell_triplets_GlobMat[iT].emplace_back(iGlobal, jGlobal, MatF(iLocal, jLocal));
328  }
329  }
330  }
331  }
332  size_triplets_GlobMat += cell_triplets_GlobMat[iT].size();
333 
334  }
335  }; // End function to construct local contributions
336 
337  // Running the local constructions in parallel
338  parallel_for(mesh->n_cells(), construct_all_local_contributions, m_use_threads);
339 
340  // Assemble local contribution into global matrix
341  triplets_ScBe.reserve(size_triplets_ScBe);
342  triplets_GlobMat.reserve(size_triplets_GlobMat);
343  for (size_t iT = 0; iT < mesh->n_cells(); iT++){
344  for (size_t i = 0; i < cell_triplets_ScBe[iT].size(); i++){
345  triplets_ScBe.push_back(cell_triplets_ScBe[iT][i]);
346  }
347  for (size_t i = 0; i < cell_triplets_GlobMat[iT].size(); i++){
348  triplets_GlobMat.push_back(cell_triplets_GlobMat[iT][i]);
349  }
350  Cell& T = *mesh->cell(iT);
351  for (size_t ilF = 0; ilF < T.n_faces(); ilF++) {
352  const size_t iF = T.face(ilF)->global_index();
353  for (size_t ik = 0; ik < m_nlocal_face_dofs; ik++) {
354  const size_t iLocal = ilF * m_nlocal_face_dofs + ik;
355  const size_t iGlobal = iF * m_nlocal_face_dofs + ik;
356  GlobRHS(iGlobal) += cell_source[iT](iLocal);
357  }
358  }
359  }
360 
361  if (m_BC.name()=="Neumann"){
362  // Neumann BC: remove a row in the matrix and fix the first degree of freedom
363  triplets_GlobMat.erase(std::remove_if(std::begin(triplets_GlobMat), std::end(triplets_GlobMat),
364  [](const auto& x) { return (x.row() == 0); }), std::end(triplets_GlobMat));
365  triplets_GlobMat.emplace_back(0, 0, 1);
366  GlobRHS(0) = 0;
367  }
368 
369  // Assemble the global linear system (without BC), and matrix to recover statically-condensed cell dofs
370  GlobMat.setFromTriplets(std::begin(triplets_GlobMat), std::end(triplets_GlobMat));
371  ScBeMat.setFromTriplets(std::begin(triplets_ScBe), std::end(triplets_ScBe));
372 
373  // Record assembly time
374 // _assembly_time = timer.elapsed().user + timer.elapsed().system;
375  _assembly_time = timer.elapsed().wall;
376 
377  }
378 
380  {
381  const Mesh* mesh = hho.get_mesh();
382  boost::timer::cpu_timer timer; // Time the matrix assembly
383  timer.start();
384 
385  //-------------- TREATMENT OF BOUNDARY CONDITIONS -------------//
386 
387  // If Dirichlet, the final system is only posed on the interior face unknowns and we have to subtract from the source
388  // term the contribution of the boundary values
389  // If Neumann, the final system is posed on all face unknowns
390 
391  size_t n_unknowns = 0;
392  size_t n_fixed_dofs = 0;
393  Eigen::SparseMatrix<double> A;
394  Eigen::VectorXd B;
395  Eigen::VectorXd UDir;
396 
397  if (m_BC.name() != "Neumann"){
398  // Dirichlet boundary conditions
399  n_unknowns = m_nnondir_face_dofs;
400  n_fixed_dofs = m_ndir_face_dofs;
401  A = GlobMat.topLeftCorner(n_unknowns, n_unknowns);
402 
403  // Boundary value: UDir corresponds to the L2 projection of the exact solution on the polynomial spaces on the Dirichlet faces (last BC.n_dir_faces() faces)
404  UDir = Eigen::VectorXd::Zero(n_fixed_dofs);
405 
406  size_t n_dir_faces = m_BC.n_dir_faces();
407  size_t n_nondir_faces = mesh->n_faces() - n_dir_faces;
408  for (size_t idF = 0; idF < n_dir_faces; idF++){
409  Face* face = mesh->face(n_nondir_faces + idF);
410  size_t iF = face->global_index();
411  QuadratureRule quadF = generate_quadrature_rule(*face, 2*m_K+2);
412  boost::multi_array<double, 2> phiF_quadF = evaluate_quad<Function>::compute(hho.FaceBasis(iF), quadF);
413  UDir.segment(idF * m_nlocal_face_dofs, m_nlocal_face_dofs) = l2_projection<HybridCore::PolyFaceBasisType>(exact_solution, hho.FaceBasis(iF), quadF, phiF_quadF);
414  }
415 
416  B = GlobRHS.segment(0, n_unknowns) - GlobMat.topRightCorner(n_unknowns, n_fixed_dofs) * UDir;
417 
418  } else {
419  // We will solve the complete system
420  n_unknowns = m_ntotal_face_dofs;
421  A = GlobMat;
422  B = GlobRHS;
423  UDir = Eigen::VectorXd::Zero(n_fixed_dofs);
424  }
425 
426  //-------------- SOLVE CONDENSED SYSTEM -------------//
427 
428  Eigen::VectorXd xF = Eigen::VectorXd::Zero(n_unknowns);
429 
430  // if (solver_type == "ma41") {
431  // Eigen::MA41<Eigen::SparseMatrix<double>, Eigen::VectorXd> solver;
432  // solver.analyzePattern(A);
433  // solver.factorize(A);
434  // xF = solver.solve(B);
435  // } else {
436  Eigen::BiCGSTAB<Eigen::SparseMatrix<double> > solver;
437  solver.compute(A);
438  xF = solver.solve(B);
439  std::cout << " [solver] #iterations: " << solver.iterations() << ", estimated error: " << solver.error() << std::endl;
440  // }
441  _solving_error = (A * xF - B).norm();
442  // Recover the fixed boundary values, cell unknowns (from static condensation/barycentric elimination)
443  Eigen::VectorXd Xh = Eigen::VectorXd::Zero(m_ntotal_dofs);
444  Xh.tail(n_fixed_dofs) = UDir;
445  Xh.segment(m_ntotal_cell_dofs, n_unknowns) = xF;
446  if (m_L>=0) {
447  Xh.head(m_ntotal_cell_dofs) = ScRHS - ScBeMat * Xh.tail(m_ntotal_face_dofs);
448  } else {
449  Xh.head(m_ntotal_cell_dofs) = ScBeMat * Xh.tail(m_ntotal_face_dofs);
450  }
451 
452  // Only Neumann: translate to get the proper average
453  if (m_BC.name()=="Neumann"){
454  // Compute average to translate
455  double average = 0.;
456  double total_measure = 0;
457  for (size_t iT = 0; iT < mesh->n_cells(); iT++){
458  Cell& T = *mesh->cell(iT);
459  total_measure += T.measure();
460  HybridCore::PolyCellBasisType basisT = hho.CellBasis(iT);
461  QuadratureRule quadT = generate_quadrature_rule(T, 2*m_K);
462  boost::multi_array<double, 2> phiT_quadT = evaluate_quad<Function>::compute(basisT, quadT);
463  for (size_t i = 0; i < basisT.dimension(); i++){
464  for (size_t iqn = 0; iqn < quadT.size(); iqn++){
465  average += quadT[iqn].w * Xh(iT * m_nlocal_cell_dofs + i) * basisT.function(i, quadT[iqn].vector());
466  }
467  }
468  }
469  double average_exact_sol = 0.0;
470  for (auto& T : mesh->get_cells()){
471  QuadratureRule quadT = generate_quadrature_rule(*T, 2 * m_Ldeg + 2);
472  for (QuadratureNode& qT : quadT){
473  average_exact_sol += qT.w * exact_solution(qT.vector());
474  }
475  }
476  average_exact_sol /= total_measure;
477 
478  // Translate the cells and faces
479  // We compute the interpolant of the constant function "average_exact_sol - average"
480  // and we translate Xh by that amount
481  std::function<double(VectorRd)> AveDiff = [&average_exact_sol,&average](VectorRd x)->double
482  { return average_exact_sol - average;};
483 
484  UVector Cst = hho.interpolate(AveDiff, m_L, m_K, 2*m_K+3);
485  Xh += Cst.asVectorXd();
486  }
487 
488  _solving_time = timer.elapsed().user + timer.elapsed().system; // Record the final solving time
489 
490  return UVector(Xh, *hho.get_mesh(), m_L, m_K);
491  }
492 
493  //********************************
494  // local diffusion matrix
495  //********************************
496 
497  Eigen::MatrixXd HHO_Diffusion::diffusion_operator(HybridCore &hho, const size_t iT, const ElementQuad &elquad) const {
498 
499  boost::timer::cpu_timer timeint;
500 
501  const auto mesh = hho.get_mesh();
502  Cell* cell = mesh->cell(iT);
503  const size_t nfacesT = cell->n_faces();
504 
505  // Total number of degrees of freedom local to this cell (cell and its adjacent faces)
506  size_t local_dofs = m_nlocal_cell_dofs + nfacesT * m_nlocal_face_dofs;
507 
508  //------------------- Initialisatons: quadratures, mass matrices... --------------------//
509 
510  // Diffusion in the cell.
511  std::function<MatrixRd(VectorRd)> kappaT = [&](VectorRd x){
512  // Constant in the cell
513  return kappa(cell->center_mass(), cell);
514  // Variable in the cell - the scheme may not provide optimal convergence rate if the diffusion is actually variable in the cell! Quadrature rules order also have to be increased
515  // If the diffusion is piecewise constant, choosing the previous version ensures that, for face integrals, its the value inside the cell that is computed (essential in case kappa is discontinuous across the faces)
516  // return kappa(x, cell);
517  };
518 
519  // QUADRATURES
520  // Cell quadrature nodes, and values of cell basis functions (up to degree K+1) and gradients thereof.
521  QuadratureRule quadT = elquad.get_quadT();
522  boost::multi_array<double, 2> phiT_quadT = elquad.get_phiT_quadT();
523  boost::multi_array<VectorRd, 2> dphiT_quadT = elquad.get_dphiT_quadT();
524 
525  // Diffusion tensor times gradient of basis functions at the quadrature nodes
526  boost::multi_array<VectorRd, 2> kappaT_dphiT_quadT( boost::extents[dphiT_quadT.shape()[0]][quadT.size()] );
527  for (size_t i = 0; i < dphiT_quadT.shape()[0]; i++){
528  for (size_t iqn = 0; iqn < quadT.size(); iqn++){
529  kappaT_dphiT_quadT[i][iqn] = kappaT(quadT[iqn].vector()) * dphiT_quadT[i][iqn];
530  }
531  }
532 
533  // Prepare to store face mass matrices:
534  // MFF[ilF]: face-face mass on face with local number ilF, up to degree K*K
535  // MFT[ilF]: face-cell mass on face with local number ilF, up to degree K*(K+1)
536  std::vector<Eigen::MatrixXd> MFF(nfacesT, Eigen::MatrixXd::Zero(m_nlocal_face_dofs, m_nlocal_face_dofs));
537  std::vector<Eigen::MatrixXd> MFT(nfacesT, Eigen::MatrixXd::Zero(m_nlocal_face_dofs, m_nhighorder_dofs));
538 
539  //-------------------- Compute PT, matrix of potential reconstruction ---------//
540 
541  // STIFNESS mass-matrix: (K_T\nabla phi_i,\nabla phi_j)_T up to degree (K+1)*(K+1)
542  Eigen::MatrixXd StiffT = compute_gram_matrix(kappaT_dphiT_quadT, dphiT_quadT, quadT, "sym");
543 
544  _itime[1] += timeint.elapsed().user + timeint.elapsed().system;
545  timeint.start();
546 
547  // Mass matrix of (phi_i,phi_j)_T for phi_i up to degree L and phi_j up to degree K+1
548  Eigen::MatrixXd MTT = compute_gram_matrix(phiT_quadT, phiT_quadT, quadT, m_nlocal_cell_dofs, m_nhighorder_dofs, "sym");
549 
550  _itime[2] += timeint.elapsed().user + timeint.elapsed().system;
551  timeint.start();
552 
553  // Row vector of (phi_j,1)_T for phi_j up to degree K+1, and LT^t*LT (used later to impose average condition on PT)
554  Eigen::VectorXd LT = (MTT.row(0)).transpose();
555  Eigen::MatrixXd LTtLT = LT * (LT.transpose());
556 
557  // Right-hand side: we start with volumetric terms (dphi_i,dphi_j)_T for phi_i up to degree K+1 and phi_j up to degree L
558  Eigen::MatrixXd BP = Eigen::MatrixXd::Zero(m_nhighorder_dofs, local_dofs);
559  BP.topLeftCorner(m_nhighorder_dofs, m_nlocal_cell_dofs) = StiffT.topLeftCorner(m_nhighorder_dofs, m_nlocal_cell_dofs);
560 
561  // Boundary terms in BP
562  for (size_t ilF = 0; ilF < nfacesT; ilF++) {
563  // Offset for face unknowns
564  const size_t offset_F = m_nlocal_cell_dofs + ilF * m_nlocal_face_dofs;
565  const auto& nTF = cell->face_normal(ilF);
566 
567  // Compute face quadrature nodes and values of basis functions (and gradients) at these nodes
568  auto quadF = elquad.get_quadF(ilF);
569  size_t nbqF = quadF.size();
570 
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  boost::multi_array<VectorRd, 2> dphiT_quadF = elquad.get_dphiT_quadF(ilF);
574 
575  // Calculation of RHS BP
576  //
577  // weight * kappaT*nTF on quadrature nodes
578  std::vector<VectorRd> quadF_kappaT_nTF(nbqF, VectorRd::Zero());
579  for (size_t iqn = 0; iqn < nbqF; iqn++){
580  quadF_kappaT_nTF[iqn] = quadF[iqn].w * kappaT(quadF[iqn].vector())*nTF;
581  }
582  for (size_t i = 1; i < m_nhighorder_dofs; i++) {
583  // We do not need i=0 because it corresponds to dphi_i=0
584  for (size_t iqn = 0; iqn < nbqF; iqn++){
585  // Contribution of test function to BP(i, .)
586  double contrib_i = dphiT_quadF[i][iqn].dot(quadF_kappaT_nTF[iqn]);
587  // Integration
588  for (size_t j = 0; j < m_nlocal_cell_dofs; j++) {
589  // Cell unknown
590  BP(i,j) -= contrib_i * phiT_quadF[j][iqn];
591  }
592  for (size_t j = 0; j < m_nlocal_face_dofs; j++) {
593  // Face unknowns
594  BP(i, offset_F + j) += contrib_i * phiF_quadF[j][iqn];
595  }
596  }
597  }
598 
599  // Face mass matrix, and cell-face mass matrix, for stabilisation term below
600  MFF[ilF] = compute_gram_matrix(phiF_quadF, phiF_quadF, quadF, "sym");
601  MFT[ilF] = compute_gram_matrix(phiF_quadF, phiT_quadF, quadF, m_nlocal_face_dofs, m_nhighorder_dofs, "nonsym");
602 
603  }
604 
605  // Compute PT
606  double scalT = StiffT.trace() / std::pow(LT.norm(), 2);
607  BP.topLeftCorner(m_nhighorder_dofs, m_nlocal_cell_dofs) += scalT * LTtLT.topLeftCorner(m_nhighorder_dofs, m_nlocal_cell_dofs);
608  Eigen::MatrixXd PT = ((StiffT+scalT*LTtLT).ldlt()).solve(BP);
609 
610  _itime[3] += timeint.elapsed().user + timeint.elapsed().system;
611  timeint.start();
612 
613  // Consistent component (K \nabla pT, \nabla pT)_T in local bilinear form
614  Eigen::MatrixXd ATF = PT.transpose() * StiffT * PT;
615 
616 
617  //-------------------- Compute stabilisation term sT ---------//
618 
619  Eigen::MatrixXd STF = Eigen::MatrixXd::Zero(local_dofs, local_dofs);
620 
621  // Cell residual delta_T^l = pi_T^l (rT uT) - u_T
622  Eigen::MatrixXd MTT_LL = MTT.topLeftCorner(m_nlocal_cell_dofs, m_nlocal_cell_dofs);
623  Eigen::MatrixXd deltaTL = MTT_LL.ldlt().solve( MTT * PT );
624  deltaTL.topLeftCorner(m_nlocal_cell_dofs, m_nlocal_cell_dofs) -= Eigen::MatrixXd::Identity(m_nlocal_cell_dofs, m_nlocal_cell_dofs);
625 
626  for (size_t ilF = 0; ilF < nfacesT; ilF++) {
627  // Two options for stabilisation: diameter of face, or ratio measure cell/measure face
628  double dTF = cell->face(ilF)->diam();
629  // double dTF = cell->measure() / cell->face(ilF)->measure();
630 
631  VectorRd xF = cell->face(ilF)->center_mass();
632 
633  // double kappa_TF = kappa(xF, cell).trace();
634 
635  const VectorRd &nTF = cell->face_normal(ilF);
636  const double kappa_TF = (kappa(xF, cell) * nTF).dot(nTF);
637 
638  // Face residual delta_TF^k = pi_F^k (rT uT) - u_F
639  Eigen::MatrixXd MFFinv = MFF[ilF].inverse();
640  Eigen::MatrixXd deltaTFK = MFFinv * MFT[ilF] * PT;
641  deltaTFK.block(0, m_nlocal_cell_dofs + ilF * m_nlocal_face_dofs, m_nlocal_face_dofs, m_nlocal_face_dofs) -=
642  Eigen::MatrixXd::Identity(m_nlocal_face_dofs, m_nlocal_face_dofs);
643 
644  // 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)
645  Eigen::MatrixXd deltaTFK_minus_deltaTL = deltaTFK - MFFinv * MFT[ilF].topLeftCorner(m_nlocal_face_dofs, m_nlocal_cell_dofs) * deltaTL;
646 
647  STF += (kappa_TF / dTF) * deltaTFK_minus_deltaTL.transpose() * MFF[ilF] * deltaTFK_minus_deltaTL;
648  }
649 
650  _itime[5] += timeint.elapsed().user + timeint.elapsed().system;
651 
652  // Adjust local bilinear form with stabilisation term
653  ATF += mesh->dim() * STF;
654 
655  return ATF;
656 
657  }
658 
659 
660  //********************************
661  // local load term
662  //********************************
663 
664  Eigen::VectorXd HHO_Diffusion::load_operator(HybridCore &hho, const size_t iT, const ElementQuad &elquad) const {
665  // Load for the cell DOFs (first indices) and face DOFs (last indices)
666  const auto mesh = hho.get_mesh();
667  Cell* cell = mesh->cell(iT);
668  size_t cell_face_dofs = m_nlocal_cell_dofs + cell->n_faces()*m_nlocal_face_dofs;
669  Eigen::VectorXd b = Eigen::VectorXd::Zero(cell_face_dofs);
670 
671  // Quadrature nodes and values of cell basis functions at these nodes
672  auto quadT = elquad.get_quadT();
673  size_t nbq = quadT.size();
674  boost::multi_array<double, 2> phiT_quadT = elquad.get_phiT_quadT();
675 
676  // Value of source times quadrature weights at the quadrature nodes
677  Eigen::ArrayXd weight_source_quad = Eigen::ArrayXd::Zero(nbq);
678  for (size_t iqn = 0; iqn < nbq; iqn++){
679  weight_source_quad(iqn) = quadT[iqn].w * source(quadT[iqn].vector(), cell);
680  }
681 
682  for (size_t i=0; i < m_nlocal_cell_dofs; i++){
683  for (size_t iqn = 0; iqn < quadT.size(); iqn++){
684  b(i) += weight_source_quad[iqn] * phiT_quadT[i][iqn];
685  }
686  }
687 
688  // Boundary values, if we have a boundary cell
689  if (cell->is_boundary()){
690  // Boundary values only on boundary Neumann faces
691  for (size_t ilF = 0; ilF < cell->n_faces(); ilF++) {
692  Face* F = cell->face(ilF);
693  if (m_BC.type(*F)=="neu"){
694  const size_t iF = F->global_index();
695  // BC on boundary faces
696  if (F->is_boundary()){
697  // Offset for face unknowns
698  const size_t offset_F = m_nlocal_cell_dofs + ilF * m_nlocal_face_dofs;
699  // Normal to the face and bases function
700  const auto& nTF = cell->face_normal(ilF);
701  const auto& basisF = hho.FaceBasis(iF);
702  // for each DOF of the boundary face
703  for (size_t i = 0; i < m_nlocal_face_dofs; i++){
704  QuadratureRule quadF = generate_quadrature_rule(*F, 2*m_K+2);
705  std::function<double(VectorRd)> Kgrad_n = [&](VectorRd p){
706  return nTF.dot(kappa(p,cell) * grad_exact_solution(p,cell)) * basisF.function(i,p);
707  };
708  for (QuadratureNode& qF : quadF){
709  b(offset_F + i) += qF.w * Kgrad_n(qF.vector());
710  }
711  }
712  }
713  }
714  }
715  }
716 
717  return b;
718  }
719 
721  const auto mesh = hho.get_mesh();
722  double value = 0.0;
723 
724  for (size_t iT = 0; iT < mesh->n_cells(); iT++) {
725  Eigen::VectorXd XTF = Xh.restr(iT);
726  value += XTF.transpose() * aT[iT] * XTF;
727  }
728 
729  return sqrt(value);
730  }
731 
732 
734 } // end of namespace HArDCore3D
735 
736 #endif //_HHO_DIFFUSION_HPP
The BoundaryConditions class provides definition of boundary conditions.
Definition: BoundaryConditions.hpp:43
const std::string type(Face &face) const
Test the boundary condition of an face.
Definition: BoundaryConditions.cpp:31
const std::string name() const
Returns the complete name of the boundary condition.
Definition: BoundaryConditions.hpp:68
const size_t n_dir_faces() const
Returns the number of Dirichlet faces.
Definition: BoundaryConditions.hpp:63
Definition: elementquad.hpp:55
Family of functions expressed as linear combination of the functions of a given basis.
Definition: basis.hpp:388
The HHO_Diffusion class provides tools to implement the HHO method for the diffusion problem.
Definition: HHO_Diffusion.hpp:64
Definition: hybridcore.hpp:174
Definition: hybridcore.hpp:87
Class to describe a mesh.
Definition: MeshND.hpp:17
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:2189
FunctionValue function(size_t i, const VectorRd &x) const
Evaluate the i-th function at point x.
Definition: basis.hpp:427
std::function< T(const VectorRd &, const Cell *)> CellFType
type for function of point. T is the return type of the function
Definition: basis.hpp:57
std::function< T(const VectorRd &)> FType
type for function of point. T is the return type of the function
Definition: basis.hpp:54
size_t dimension() const
Dimension of the family. This is actually the number of functions in the family, not necessarily line...
Definition: basis.hpp:421
Eigen::Matrix3d MatrixRd
Definition: basis.hpp:51
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:339
static void parallel_for(unsigned nb_elements, std::function< void(size_t start, size_t end)> functor, bool use_threads=true)
Generic function to execute threaded processes.
Definition: parallel_for.hpp:58
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 get_itime(size_t idx) const
various intermediate assembly times
Definition: HHO_Diffusion.hpp:105
double EnergyNorm(HybridCore &hho, const UVector Xh)
Discrete energy norm (associated to the diffusion operator) of an hybrid function.
Definition: HHO_Diffusion.hpp:720
const size_t get_ntotal_face_dofs()
Total number of face DOFs over the entire mesh.
Definition: HHO_Diffusion.hpp:119
const size_t get_ndir_face_dofs()
Total number of face DOFs for Dirichlet faces.
Definition: HHO_Diffusion.hpp:121
UVector solve(HybridCore &hho)
Definition: HHO_Diffusion.hpp:379
const size_t get_nlocal_cell_dofs()
Number of DOFs in each cell.
Definition: HHO_Diffusion.hpp:111
const size_t get_nlocal_face_dofs()
Number of DOFs on each face.
Definition: HHO_Diffusion.hpp:113
void assemble(HybridCore &hho)
Assemble and solve the scheme.
Definition: HHO_Diffusion.hpp:209
double get_solving_error() const
residual after solving the scheme
Definition: HHO_Diffusion.hpp:100
const size_t get_ntotal_dofs()
Total number of degrees of freedom over the entire mesh.
Definition: HHO_Diffusion.hpp:123
double get_assembly_time() const
cpu time to assemble the scheme
Definition: HHO_Diffusion.hpp:90
const size_t get_nhighorder_dofs()
Number of DOFs per cell for high-order (K+1) polynomials.
Definition: HHO_Diffusion.hpp:115
const size_t get_ntotal_cell_dofs()
Total number of cell DOFs over the entire mesh.
Definition: HHO_Diffusion.hpp:117
double get_solving_time() const
cpu time to solve the scheme
Definition: HHO_Diffusion.hpp:95
HHO_Diffusion(HybridCore &hho, size_t K, int L, CellFType< MatrixRd > 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_Diffusion.hpp:179
size_t const DimPoly(int m)
const boost::multi_array< VectorRd, 2 > & get_dphiT_quadF(size_t ilF) const
Returns values of gradients of cell basis functions at face quadrature nodes, for face with local num...
Definition: elementquad.hpp:103
Eigen::VectorXd compute_weights(size_t iT) const
Computes the weights to get cell values from face values when l=-1.
Definition: hybridcore.cpp:175
Eigen::VectorXd restr(size_t iT) const
Extract the restriction of the unknowns corresponding to cell iT and its faces.
Definition: hybridcore.cpp:28
const boost::multi_array< double, 2 > & get_phiT_quadF(size_t ilF) const
Returns values of cell basis functions at face quadrature nodes, for face with local number ilF.
Definition: elementquad.hpp:91
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:85
const boost::multi_array< double, 2 > get_phiF_quadF(size_t ilF) const
Returns values of face basis functions at face quadrature nodes, for face with local number ilF.
Definition: elementquad.hpp:97
const PolyFaceBasisType & FaceBasis(size_t iF) const
Return face basis for face with global index iF.
Definition: hybridcore.hpp:215
const QuadratureRule & get_quadF(size_t ilF) const
Returns quadrature rules on face with local number ilF.
Definition: elementquad.hpp:73
const boost::multi_array< double, 2 > & get_phiT_quadT() const
Returns values of cell basis functions at cell quadrature nodes.
Definition: elementquad.hpp:79
Eigen::VectorXd & asVectorXd() const
Return the values as an Eigen vector.
Definition: hybridcore.hpp:97
UVector interpolate(const ContinuousFunction &f, const int deg_cell, const size_t deg_face, size_t doe) const
Compute the interpolant in the discrete space of a continuous function.
Definition: hybridcore.hpp:298
const QuadratureRule & get_quadT() const
Returns quadrature rules in cell.
Definition: elementquad.hpp:67
const Mesh * get_mesh() const
Returns a pointer to the mesh.
Definition: hybridcore.hpp:198
const PolyCellBasisType & CellBasis(size_t iT) const
Return cell basis for element with global index iT.
Definition: hybridcore.hpp:207
std::vector< Cell< dimension > * > get_cells() const
lists the cells in the mesh.
Definition: MeshND.hpp:88
std::size_t n_faces() const
number of faces in the mesh.
Definition: MeshND.hpp:59
Face< dimension > * face(std::size_t index) const
get a constant pointer to a face using its global index
Definition: MeshND.hpp:196
std::size_t n_cells() const
number of cells in the mesh.
Definition: MeshND.hpp:60
Cell< dimension > * cell(std::size_t index) const
get a constant pointer to a cell using its global index
Definition: MeshND.hpp:197
QuadratureRule generate_quadrature_rule(const Cell &T, const int doe, const bool force_split)
Generate quadrature rule on mesh element.
Definition: quadraturerule.cpp:11
std::vector< QuadratureNode > QuadratureRule
Definition: quadraturerule.hpp:61
Definition: ddr-magnetostatics.hpp:40
MeshND::VectorRd< 2 > VectorRd
Definition: Mesh2D.hpp:14
MeshND::Face< 2 > Face
Definition: Mesh2D.hpp:12
MeshND::Cell< 2 > Cell
Definition: Mesh2D.hpp:13
Description of one node and one weight from a quadrature rule.
Definition: quadraturerule.hpp:47