HArD::Core3D
Hybrid Arbitrary Degree::Core 3D - Library to implement 3D schemes with vertex, edge, face and cell polynomials as unknowns
Loading...
Searching...
No Matches
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
52namespace HArDCore3D {
53
58 // ----------------------------------------------------------------------------
59 // Class definition
60 // ----------------------------------------------------------------------------
61
63
65
66 public:
70 size_t K,
71 int L,
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);
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;
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 }
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);
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 }
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;
328 }
329 }
330 }
331 }
333
334 }
335 }; // End function to construct local contributions
336
337 // Running the local constructions in parallel
339
340 // Assemble local contribution into global matrix
343 for (size_t iT = 0; iT < mesh->n_cells(); iT++){
344 for (size_t i = 0; i < cell_triplets_ScBe[iT].size(); i++){
346 }
347 for (size_t i = 0; i < cell_triplets_GlobMat[iT].size(); 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();
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();
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 }
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++){
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)
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, .)
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
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
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++){
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++){
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:129
const std::string type(const Face &face) const
Test the boundary condition of an face.
Definition BoundaryConditions.cpp:81
const std::string name() const
Returns the complete name of the boundary condition.
Definition BoundaryConditions.hpp:178
const size_t n_dir_faces() const
Returns the number of Dirichlet faces.
Definition BoundaryConditions.hpp:160
Definition elementquad.hpp:55
Family of functions expressed as linear combination of the functions of a given basis.
Definition basis.hpp:389
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:2224
std::function< T(const VectorRd &, const Cell *)> CellFType
type for function of point. T is the return type of the function
Definition basis.hpp:58
std::function< T(const VectorRd &)> FType
type for function of point. T is the return type of the function
Definition basis.hpp:55
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:353
@ Matrix
Definition basis.hpp:67
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)
Eigen::VectorXd & asVectorXd() const
Return the values as an Eigen vector.
Definition hybridcore.hpp:97
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_faces() const
number of faces in the mesh.
Definition MeshND.hpp:59
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
std::vector< Cell< dimension > * > get_cells() const
lists the cells in the mesh.
Definition MeshND.hpp:88
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 PastixInterface.hpp:16
Definition ddr-magnetostatics.hpp:41
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