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_LocVarDiff.hpp
Go to the documentation of this file.
1// Implementation of the HHO scheme in 3D 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. 2019, 516p.
19 * url: https://hal.archives-ouvertes.fr/hal-02151813.
20 *
21 * If you use this code or part of it for a scientific publication, please cite the book
22 * above as a reference for the implementation.
23 *
24 */
25
26#ifndef _HHO_LOCVARDIFF_HPP
27#define _HHO_LOCVARDIFF_HPP
28
29#include <functional>
30#include <utility>
31#include <iostream>
32
33#include <boost/timer/timer.hpp>
34
35// Matrices and linear solvers
36#include <Eigen/Sparse>
37#include <Eigen/Dense>
38//#include "Eigen/MA41.cpp"
39
40#include <hybridcore.hpp>
41#include <elementquad.hpp>
42#include <parallel_for.hpp>
43#include <TestCase/TestCase.hpp>
45
51namespace HArDCore3D {
52
57 // ----------------------------------------------------------------------------
58 // Class definition
59 // ----------------------------------------------------------------------------
60
62
64 public:
68 size_t K,
69 int L,
71 size_t deg_kappa,
72 CellFType<double> source,
74 FType<double> exact_solution,
75 CellFType<VectorRd> grad_exact_solution,
76 std::string solver_type,
77 bool use_threads,
78 std::ostream & output = std::cout
79 );
80
82 void assemble(HybridCore& hho);
84
86 double EnergyNorm(HybridCore& hho, const UVector Xh);
87
89 inline double get_assembly_time() const
90 {
91 return double(_assembly_time) * pow(10, -9);
92 };
94 inline double get_solving_time() const
95 {
96 return double(_solving_time) * pow(10, -9);
97 };
99 inline double get_solving_error() const
100 {
101 return _solving_error;
102 };
104 inline double get_itime(size_t idx) const
105 {
106 return double(_itime[idx]) * pow(10, -9);
107 };
108
110 const inline size_t get_nlocal_cell_dofs() { return m_nlocal_cell_dofs; }
112 const inline size_t get_nlocal_face_dofs() { return m_nlocal_face_dofs; }
114 const inline size_t get_nhighorder_dofs() { return m_nhighorder_dofs; }
116 const inline size_t get_ntotal_cell_dofs() { return m_ntotal_cell_dofs; }
118 const inline size_t get_ntotal_face_dofs() { return m_ntotal_face_dofs; }
120 const inline size_t get_ndir_face_dofs() { return m_ndir_face_dofs; }
122 const inline size_t get_ntotal_dofs() { return m_ntotal_dofs; }
123
124 private:
126 Eigen::MatrixXd diffusion_operator(HybridCore& hho, const size_t iT, const ElementQuad& elquad) const;
127
129 Eigen::VectorXd load_operator(HybridCore& hho, const size_t iT, const ElementQuad &elquad) const;
130
131 // Reference to the HybridCore structure
132 HybridCore& m_hho;
133
134 // Degrees on faces and in cells
135 size_t m_K;
136 int m_L;
137 size_t m_Ldeg;
138
139 // Data
140 const CellFType<MatrixRd> kappa;
141 size_t _deg_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_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)
180 : m_hho(hho),
181 m_K(K),
182 m_L(L),
183 m_Ldeg(std::max(L,0)),
184 kappa(kappa),
185 _deg_kappa(deg_kappa),
186 source(source),
187 m_BC(BC),
188 exact_solution(exact_solution),
189 grad_exact_solution(grad_exact_solution),
190 solver_type(solver_type),
191 m_use_threads(use_threads),
192 m_output(output),
193 m_nlocal_cell_dofs(DimPoly<Cell>(m_Ldeg)),
194 m_nlocal_face_dofs(DimPoly<Face>(m_K)),
195 m_nhighorder_dofs(DimPoly<Cell>(m_K+1)),
196 m_ntotal_cell_dofs(m_nlocal_cell_dofs * m_hho.get_mesh()->n_cells()),
197 m_ntotal_face_dofs(m_nlocal_face_dofs * m_hho.get_mesh()->n_faces()),
198 m_ndir_face_dofs(m_nlocal_face_dofs * m_BC.n_dir_faces()),
199 m_nnondir_face_dofs(m_nlocal_face_dofs * m_hho.get_mesh()->n_faces() - m_ndir_face_dofs),
200 m_ntotal_dofs(m_ntotal_cell_dofs + m_ntotal_face_dofs),
201 GlobRHS(Eigen::VectorXd::Zero(m_ntotal_face_dofs)),
202 ScRHS(Eigen::VectorXd::Zero(m_ntotal_cell_dofs))
203 {
204 m_output << "[HHO_LocVarDiff] Initializing" << std::endl;
205 GlobMat.resize(m_ntotal_face_dofs, m_ntotal_face_dofs);
206 ScBeMat.resize(m_ntotal_cell_dofs, m_ntotal_face_dofs);
207 // Do nothing
208 }
209
211
212 boost::timer::cpu_timer timer; // Time the matrix assembly
213 timer.start();
214 const Mesh* mesh = hho.get_mesh();
215
216 //--------------- PREPARE SYSTEM ------------------------//
217
218 // Global triplets for: system matrix, static condensaion/barycentric elimination
219 std::vector<Eigen::Triplet<double>> triplets_GlobMat;
220 std::vector<Eigen::Triplet<double>> triplets_ScBe;
221
222 // Local bilinear form, triplets and source term (one per cell)
223 aT.resize(mesh->n_cells());
224 std::vector<std::vector<Eigen::Triplet<double>>> cell_triplets_GlobMat;
225 std::vector<std::vector<Eigen::Triplet<double>>> cell_triplets_ScBe;
226 std::vector<Eigen::VectorXd> cell_source(mesh->n_cells());
227 cell_triplets_GlobMat.resize(mesh->n_cells());
228 cell_triplets_ScBe.resize(mesh->n_cells());
229 size_t size_triplets_GlobMat = 0;
230 size_t size_triplets_ScBe = 0;
231
232 //-------------- ASSEMBLE LOCAL CONTRIBUTIONS -------------//
233
234 // Function to create local contribution between cell start and cell end-1
235 std::function<void(size_t, size_t)> construct_all_local_contributions
236 = [&](size_t start, size_t end)->void
237 {
238 for (size_t iT = start; iT < end; iT++) {
239 Cell* iCell = mesh->cell(iT);
240
241 // Total number of face degrees of freedom local to this cell (adjacent faces to the cell)
242 size_t nlocal_faces = iCell->n_faces();
243 size_t face_dofs = nlocal_faces * m_nlocal_face_dofs;
244
245 // Local bilinear form and source term
246 size_t doeT = std::max( std::max(m_K,m_Ldeg) + m_K+1 , 2*m_K + _deg_kappa );
247 size_t doeF = 2*m_K + 1;
249
250 aT[iT] = diffusion_operator(hho, iT, elquad);
251 Eigen::VectorXd bT = load_operator(hho, iT, elquad);
252
253 // Local matrix and right-hand side on the face unknowns
254 Eigen::MatrixXd MatF = Eigen::MatrixXd::Zero(face_dofs,face_dofs);
255 cell_source[iT] = Eigen::VectorXd::Zero(face_dofs);
256
257 if (m_L>=0) {
258 // STATIC CONDENSATION OF ELEMENT UNKNOWNS
259
260 // Perform static condensation
261 Eigen::MatrixXd ATT = aT[iT].topLeftCorner(m_nlocal_cell_dofs, m_nlocal_cell_dofs);
262 Eigen::MatrixXd ATF = aT[iT].topRightCorner(m_nlocal_cell_dofs, face_dofs);
263 Eigen::MatrixXd AFF = aT[iT].bottomRightCorner(face_dofs, face_dofs);
264
265 Eigen::PartialPivLU<Eigen::MatrixXd> invATT;
266 invATT.compute(ATT);
267
268 Eigen::MatrixXd invATT_ATF = invATT.solve(ATF);
269 Eigen::VectorXd invATT_bTcell = invATT.solve(bT.head(m_nlocal_cell_dofs));
270 MatF = AFF - ATF.transpose() * invATT_ATF;
271
272 cell_source[iT] = bT.tail(face_dofs) - ATF.transpose() * invATT_bTcell;
273
274 // Assemble local triplets for static condensation operator
275 ScRHS.segment(iT * m_nlocal_cell_dofs, m_nlocal_cell_dofs) = invATT_bTcell;
276 for (size_t i = 0; i < m_nlocal_cell_dofs; i++) {
277 for (size_t jlF = 0; jlF < nlocal_faces; jlF++) {
278 const size_t jF = iCell->face(jlF)->global_index();
279 for (size_t jk = 0; jk < m_nlocal_face_dofs; jk++) {
280 const size_t jLocal = jlF * m_nlocal_face_dofs + jk;
281 const size_t jGlobal = jF * m_nlocal_face_dofs + jk;
282 cell_triplets_ScBe[iT].emplace_back(iT * m_nlocal_cell_dofs + i, jGlobal, invATT_ATF(i, jLocal));
283 }
284 }
285 }
287
288 } else {
289 // BARYCENTRIC ELIMINATION OF ELEMENT UNKNOWNS
290 // 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
291 // 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.
292 Eigen::MatrixXd red_matT = Eigen::MatrixXd::Zero(1+nlocal_faces,nlocal_faces);
293 red_matT.row(0) = hho.compute_weights(iT);
294 VectorRd xT = iCell->center_mass();
295 double phiT_cst = hho.CellBasis(iT).function(0, xT);
296 for (size_t ilF = 0; ilF < nlocal_faces; ilF++){
297 VectorRd xF = iCell->face(ilF)->center_mass();
298 size_t iF = iCell->face(ilF)->global_index();
299 double phiF_cst = hho.FaceBasis(iF).function(0, xF);
301 }
302 red_matT.bottomRightCorner(nlocal_faces,nlocal_faces) = Eigen::MatrixXd::Identity(nlocal_faces,nlocal_faces);
303
304 cell_source[iT] = red_matT.transpose() * bT;
305 MatF = red_matT.transpose() * aT[iT] * red_matT;
306
307 // Assemble local triplets for barycentric combination to recover cell unknown
308 for (size_t jlF = 0; jlF < nlocal_faces; jlF++) {
309 const size_t jF = iCell->face(jlF)->global_index();
310 const size_t jGlobal = jF * m_nlocal_face_dofs;
311 cell_triplets_ScBe[iT].emplace_back(iT, jGlobal, red_matT(0,jlF));
312 }
314
315 }
316
317 // Assemble local triplets for scheme's matrix and source term
318 for (size_t ilF = 0; ilF < nlocal_faces; ilF++) {
319 const size_t iF = iCell->face(ilF)->global_index();
320 for (size_t ik = 0; ik < m_nlocal_face_dofs; ik++) {
321 const size_t iLocal = ilF * m_nlocal_face_dofs + ik;
322 const size_t iGlobal = iF * m_nlocal_face_dofs + ik;
323 for (size_t jlF = 0; jlF < nlocal_faces; jlF++) {
324 const size_t jF = iCell->face(jlF)->global_index();
325 for (size_t jk = 0; jk < m_nlocal_face_dofs; jk++) {
326 const size_t jLocal = jlF * m_nlocal_face_dofs + jk;
327 const size_t jGlobal = jF * m_nlocal_face_dofs + jk;
329 }
330 }
331 }
332 }
334
335 }
336 }; // End function to construct local contributions
337
338 // Running the local constructions in parallel
340
341 // Assemble local contribution into global matrix
344 for (size_t iT = 0; iT < mesh->n_cells(); iT++){
345 for (size_t i = 0; i < cell_triplets_ScBe[iT].size(); i++){
347 }
348 for (size_t i = 0; i < cell_triplets_GlobMat[iT].size(); i++){
350 }
351 Cell& T = *mesh->cell(iT);
352 for (size_t ilF = 0; ilF < T.n_faces(); ilF++) {
353 const size_t iF = T.face(ilF)->global_index();
354 for (size_t ik = 0; ik < m_nlocal_face_dofs; ik++) {
355 const size_t iLocal = ilF * m_nlocal_face_dofs + ik;
356 const size_t iGlobal = iF * m_nlocal_face_dofs + ik;
357 GlobRHS(iGlobal) += cell_source[iT](iLocal);
358 }
359 }
360 }
361
362 if (m_BC.name()=="Neumann"){
363 // Neumann BC: remove a row in the matrix and fix the first degree of freedom
364 triplets_GlobMat.erase(std::remove_if(std::begin(triplets_GlobMat), std::end(triplets_GlobMat),
365 [](const auto& x) { return (x.row() == 0); }), std::end(triplets_GlobMat));
366 triplets_GlobMat.emplace_back(0, 0, 1);
367 GlobRHS(0) = 0;
368 }
369
370 // Assemble the global linear system (without BC), and matrix to recover statically-condensed cell dofs
371 GlobMat.setFromTriplets(std::begin(triplets_GlobMat), std::end(triplets_GlobMat));
372 ScBeMat.setFromTriplets(std::begin(triplets_ScBe), std::end(triplets_ScBe));
373
374 // Record assembly time
375// _assembly_time = timer.elapsed().user + timer.elapsed().system;
376 _assembly_time = timer.elapsed().wall;
377
378 }
379
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_LocVarDiff::diffusion_operator(HybridCore &hho, const size_t iT, const ElementQuad &elquad) const
498 {
499
500 boost::timer::cpu_timer timeint;
501
502 const auto mesh = hho.get_mesh();
503 const size_t dimPKcell = DimPoly<Cell>(m_K);
504 const size_t dimPKcell_vec = mesh->dim() * dimPKcell;
505 Cell* cell = mesh->cell(iT);
506 const size_t nfacesT = cell->n_faces();
507
508 // Total number of degrees of freedom local to this cell (cell and its adjacent faces)
509 size_t local_dofs = m_nlocal_cell_dofs + nfacesT * m_nlocal_face_dofs;
510
511 //------------------- Initialisatons: quadratures, mass matrices... --------------------//
512
513 // Note: representing the gradient reconstruction GT supposes a basis for (P^k)^d. This basis can be
514 // built from the basis (phi_i)_i of P^k, by working component-by-component:
515 // (phi_1 e_1, ..., phi_N e_1, phi_1 e_2, ..., phi_N e_2, ...),
516 // where e_1=[1 0], e_2=[0 1]... is the canonical basis of R^d
517 // Hence, the first dimPKcell functions are on the first component of R^d, the next dimPKcell on the second
518 // component etc.
519 // We do not explicitly build this basis function, but we compute the values of these functions at the
520 // quadrature nodes, using the values computed for the scalar basis functions
521
522
523 // QUADRATURES
524 // Cell quadrature nodes, and values of cell basis functions (up to degree K+1) and gradients thereof.
525 QuadratureRule quadT = elquad.get_quadT();
526 boost::multi_array<double, 2> phiT_quadT = elquad.get_phiT_quadT();
527 boost::multi_array<VectorRd, 2> dphiT_quadT = elquad.get_dphiT_quadT();
528
529 // Vector basis functions (up to degree K) at the quadrature nodes.
530 boost::multi_array<VectorRd, 2> vec_phiT_quadT = elquad.get_vec_phiT_quadT(m_K);
531
532 // Diffusion tensor at the quadrature nodes
533 std::vector<MatrixRd> kappaT_quadT(quadT.size());
534 std::transform(quadT.begin(), quadT.end(), kappaT_quadT.begin(),
535 [this,&cell](QuadratureNode qr) -> Eigen::MatrixXd { return kappa(qr.vector(), cell); });
536
537 // MASS MATRICES
538 // Scalar cell mass matrix (phi_i,phi_j)_T up to degree max(L,K) * (K+1), and
539 // Vector cell mass matrix (Phi_i,Phi_j)_T up to degree K*K [this one is block diagonal]
540 size_t maxdimPKL = std::max(m_nlocal_cell_dofs, dimPKcell);
541
542 _itime[0] += timeint.elapsed().user + timeint.elapsed().system;
543 timeint.start();
544
545 Eigen::MatrixXd MTT = compute_gram_matrix(phiT_quadT, phiT_quadT, quadT, maxdimPKL, m_nhighorder_dofs, "sym");
546 Eigen::MatrixXd VecMTT = Eigen::MatrixXd::Zero(dimPKcell_vec, dimPKcell_vec);
547 for (size_t r=0; r < mesh->dim(); r++){
549 }
550
551 // Face mass matrices:
552 // MFF[ilF]: face-face mass on face with local number ilF, up to degree K*K
553 // MFT[ilF]: face-cell mass on face with local number ilF, up to degree K*(K+1)
554 // MTT_on_F[ilF]: cell-cell mass on face with local number ilF, up to degree K*L
555 std::vector<Eigen::MatrixXd> MFF(nfacesT, Eigen::MatrixXd::Zero(m_nlocal_face_dofs, m_nlocal_face_dofs));
556 std::vector<Eigen::MatrixXd> MFT(nfacesT, Eigen::MatrixXd::Zero(m_nlocal_face_dofs, m_nhighorder_dofs));
557 std::vector<Eigen::MatrixXd> MTT_on_F(nfacesT, Eigen::MatrixXd::Zero(dimPKcell, m_nlocal_cell_dofs));
558
559 for (size_t ilF = 0; ilF < nfacesT; ilF++) {
560
561 // Face quadrature nodes and values of cell and face basis functions (and gradients) at these nodes
562 auto quadF = elquad.get_quadF(ilF);
563 boost::multi_array<double, 2> phiT_quadF = elquad.get_phiT_quadF(ilF);
564 boost::multi_array<double, 2> phiF_quadF = elquad.get_phiF_quadF(ilF);
565
566 // Mass matrices
569 MTT_on_F[ilF] = compute_gram_matrix(phiT_quadF, phiT_quadF, quadF, dimPKcell, m_nlocal_cell_dofs, "nonsym");
570 }
571
572 _itime[1] += timeint.elapsed().user + timeint.elapsed().system;
573 timeint.start();
574
575 // STIFNESS mass-matrices (and the like): (\nabla phi_i,\nabla phi_j)_T up to degree (K+1)*(K+1)
576 // and (\nabla phi_i, Phi_j)_T up to degree (K+1)*K
577 Eigen::MatrixXd StiffT = compute_gram_matrix(dphiT_quadT, dphiT_quadT, quadT, "sym");
578 Eigen::MatrixXd MdphiT_PhiT = compute_gram_matrix(dphiT_quadT, vec_phiT_quadT, quadT, "nonsym");
579
580 _itime[2] += timeint.elapsed().user + timeint.elapsed().system;
581 timeint.start();
582
583 //-------------------- Compute GT, matrix of full gradient reconstruction ---------//
584
585 // Right-hand side, starting with volumetric term (Phi_i, \nabla phi_j)_T
586 Eigen::MatrixXd RHS_GT = Eigen::MatrixXd::Zero(dimPKcell_vec, local_dofs);
587 RHS_GT.topLeftCorner(dimPKcell_vec, m_nlocal_cell_dofs) = (MdphiT_PhiT.topLeftCorner(m_nlocal_cell_dofs, dimPKcell_vec)).transpose();
588
589 // Boundary terms
590 for (size_t r=0; r < mesh->dim(); r++){
591 for (size_t ilF = 0; ilF < nfacesT; ilF++) {
592 // Offset for face unknowns
593 const size_t offset_F = m_nlocal_cell_dofs + ilF * m_nlocal_face_dofs;
594 const auto& nTF = cell->face_normal(ilF);
595
596 // Contribution of cell unknowns, and then face unknowns on F
597 RHS_GT.block(r*dimPKcell, 0, dimPKcell, m_nlocal_cell_dofs) -= nTF(r) * MTT_on_F[ilF];
598 RHS_GT.block(r*dimPKcell, offset_F, dimPKcell, m_nlocal_face_dofs) +=
599 nTF(r) * (MFT[ilF].topLeftCorner(m_nlocal_face_dofs, dimPKcell)).transpose();
600 }
601 }
602
603 // Compute GT
604 Eigen::MatrixXd GT = (VecMTT.ldlt()).solve(RHS_GT);
605
606 //------------- Consistent contribution (K GT, GT)_T to the local bilinear form ------//
607
608
609 // Weighted mass matrix (K Phi_i, Phi_j)_T of the basis (Phi_i)_i of (P^k)^d
610 // We start by creating K Phi_i at the quadrature nodes
611 boost::multi_array<VectorRd, 2> kappa_vec_phiT_quadT;
612 kappa_vec_phiT_quadT.resize( boost::extents[dimPKcell_vec][quadT.size()] );
613 for (size_t i = 0; i < dimPKcell_vec; i++){
614 for (size_t iqn = 0; iqn < quadT.size(); iqn++){
616 }
617 }
618 _itime[3] += timeint.elapsed().user + timeint.elapsed().system;
619 timeint.start();
620
622
623 _itime[4] += timeint.elapsed().user + timeint.elapsed().system;
624 timeint.start();
625
626 Eigen::MatrixXd ATF = GT.transpose() * kappaVecMTT * GT;
627
628
629 //------------- Compute PT, matrix of potential reconstruction, using GT ------//
630
631 // We write that \nabla pT = projection on \nabla P^{k+1} of GT, and add the closure relation:
632 // (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
633
634 // Right-hand side, starting with volumetric term
635 Eigen::MatrixXd RHS_PT = MdphiT_PhiT * GT;
636
637 // Vector LT of (phi_j,1)_T for phi_j up to degree K+1, and LT^t*LT, for the closure relation
638 Eigen::VectorXd LT = (MTT.row(0)).transpose();
639 Eigen::MatrixXd LTtLT = LT * (LT.transpose());
640 double scalT = StiffT.trace() / std::pow(LT.norm(), 2);
641
642 // Add closure relation and compute PT
643 RHS_PT.topLeftCorner(m_nhighorder_dofs, m_nlocal_cell_dofs) +=
644 scalT * LTtLT.topLeftCorner(m_nhighorder_dofs, m_nlocal_cell_dofs);
645 Eigen::MatrixXd PT = ((StiffT + scalT*LTtLT).ldlt()).solve(RHS_PT);
646
647
648 //-------------------- Compute stabilisation term sT ---------//
649
650 Eigen::MatrixXd STF = Eigen::MatrixXd::Zero(local_dofs, local_dofs);
651
652 // Cell residual delta_T^l = pi_T^l (rT uT) - u_T
653
654 Eigen::MatrixXd MTT_LKp1 = MTT.topLeftCorner(m_nlocal_cell_dofs, m_nhighorder_dofs);
655 Eigen::MatrixXd MTT_LL = MTT.topLeftCorner(m_nlocal_cell_dofs, m_nlocal_cell_dofs);
656 Eigen::MatrixXd deltaTL = MTT_LL.ldlt().solve( MTT_LKp1 * PT );
657 deltaTL.topLeftCorner(m_nlocal_cell_dofs, m_nlocal_cell_dofs) -= Eigen::MatrixXd::Identity(m_nlocal_cell_dofs, m_nlocal_cell_dofs);
658
659 for (size_t ilF = 0; ilF < nfacesT; ilF++) {
660 // Two options for stabilisation: diameter of face, or ratio measure cell/measure face
661 double dTF = cell->face(ilF)->diam();
662 // double dTF = cell->measure() / cell->face(ilF)->measure();
663
664 VectorRd xF = cell->face(ilF)->center_mass();
665
666 // auto kappa_TF = kappa(xF, cell).trace();
667
668 const VectorRd &nTF = cell->face_normal(ilF);
669 const double kappa_TF = (kappa(xF, cell) * nTF).dot(nTF);
670
671 // Face residual delta_TF^k = pi_F^k (rT uT) - u_F
672 Eigen::MatrixXd MFFinv = MFF[ilF].inverse();
673 Eigen::MatrixXd deltaTFK = MFFinv * MFT[ilF] * PT;
674 deltaTFK.block(0, m_nlocal_cell_dofs + ilF * m_nlocal_face_dofs, m_nlocal_face_dofs, m_nlocal_face_dofs) -=
675 Eigen::MatrixXd::Identity(m_nlocal_face_dofs, m_nlocal_face_dofs);
676
677 // 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)
678 Eigen::MatrixXd deltaTFK_minus_deltaTL = deltaTFK - MFFinv * MFT[ilF].topLeftCorner(m_nlocal_face_dofs, m_nlocal_cell_dofs) * deltaTL;
679
681
682 }
683
684 _itime[5] += timeint.elapsed().user + timeint.elapsed().system;
685
686
687
688 // Adjust local bilinear form with stabilisation term
689 ATF += mesh->dim() * STF;
690
691 return ATF;
692
693 }
694
695
696 //********************************
697 // local load term
698 //********************************
699
700 Eigen::VectorXd HHO_LocVarDiff::load_operator(HybridCore &hho, const size_t iT, const ElementQuad &elquad) const {
701 // Load for the cell DOFs (first indices) and face DOFs (last indices)
702 const auto mesh = hho.get_mesh();
703 Cell* cell = mesh->cell(iT);
704 size_t cell_face_dofs = m_nlocal_cell_dofs + cell->n_faces()*m_nlocal_face_dofs;
705 Eigen::VectorXd b = Eigen::VectorXd::Zero(cell_face_dofs);
706
707 // Quadrature nodes and values of cell basis functions at these nodes
708 auto quadT = elquad.get_quadT();
709 size_t nbq = quadT.size();
710 boost::multi_array<double, 2> phiT_quadT = elquad.get_phiT_quadT();
711
712 // Value of source times quadrature weights at the quadrature nodes
713 Eigen::ArrayXd weight_source_quad = Eigen::ArrayXd::Zero(nbq);
714 for (size_t iqn = 0; iqn < nbq; iqn++){
715 weight_source_quad(iqn) = quadT[iqn].w * source(quadT[iqn].vector(), cell);
716 }
717
718 for (size_t i=0; i < m_nlocal_cell_dofs; i++){
719 for (size_t iqn = 0; iqn < quadT.size(); iqn++){
721 }
722 }
723
724 // Boundary values, if we have a boundary cell
725 if (cell->is_boundary()){
726 // Boundary values only on boundary Neumann faces
727 for (size_t ilF = 0; ilF < cell->n_faces(); ilF++) {
728 Face* F = cell->face(ilF);
729 if (m_BC.type(*F)=="neu"){
730 const size_t iF = F->global_index();
731 // BC on boundary faces
732 if (F->is_boundary()){
733 // Offset for face unknowns
734 const size_t offset_F = m_nlocal_cell_dofs + ilF * m_nlocal_face_dofs;
735 // Normal to the face and bases function
736 const auto& nTF = cell->face_normal(ilF);
737 const auto& basisF = hho.FaceBasis(iF);
738 // for each DOF of the boundary face
739 for (size_t i = 0; i < m_nlocal_face_dofs; i++){
741 std::function<double(VectorRd)> Kgrad_n = [&](VectorRd p){
742 return nTF.dot(kappa(p,cell) * grad_exact_solution(p,cell)) * basisF.function(i,p);
743 };
744 for (QuadratureNode& qF : quadF){
745 b(offset_F + i) += qF.w * Kgrad_n(qF.vector());
746 }
747 }
748 }
749 }
750 }
751 }
752
753 return b;
754 }
755
757 const auto mesh = hho.get_mesh();
758 double value = 0.0;
759
760 for (size_t iT = 0; iT < mesh->n_cells(); iT++) {
761 Eigen::VectorXd XTF = Xh.restr(iT);
762 value += XTF.transpose() * aT[iT] * XTF;
763 }
764
765 return sqrt(value);
766 }
767
768
770} // end of namespace HArDCore3D
771
772#endif //_HHO_LOCVARDIFF_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_LocVarDiff class provides tools to implement the HHO method for the diffusion problem.
Definition HHO_LocVarDiff.hpp:63
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::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_solving_time() const
cpu time to solve the scheme
Definition HHO_LocVarDiff.hpp:94
UVector solve(HybridCore &hho)
Definition HHO_LocVarDiff.hpp:380
double EnergyNorm(HybridCore &hho, const UVector Xh)
Discrete energy norm (associated to the diffusion operator) of an hybrid function.
Definition HHO_LocVarDiff.hpp:756
double get_solving_error() const
residual after solving the scheme
Definition HHO_LocVarDiff.hpp:99
double get_assembly_time() const
cpu time to assemble the scheme
Definition HHO_LocVarDiff.hpp:89
const size_t get_nlocal_face_dofs()
Number of DOFs on each face.
Definition HHO_LocVarDiff.hpp:112
void assemble(HybridCore &hho)
Assemble and solve the scheme.
Definition HHO_LocVarDiff.hpp:210
const size_t get_nhighorder_dofs()
Number of DOFs per cell for high-order (K+1) polynomials.
Definition HHO_LocVarDiff.hpp:114
const size_t get_ntotal_cell_dofs()
Total number of cell DOFs over the entire mesh.
Definition HHO_LocVarDiff.hpp:116
const size_t get_ntotal_dofs()
Total number of degrees of freedom over the entire mesh.
Definition HHO_LocVarDiff.hpp:122
const size_t get_nlocal_cell_dofs()
Number of DOFs in each cell.
Definition HHO_LocVarDiff.hpp:110
const size_t get_ndir_face_dofs()
Total number of face DOFs for Dirichlet faces.
Definition HHO_LocVarDiff.hpp:120
double get_itime(size_t idx) const
various intermediate assembly times
Definition HHO_LocVarDiff.hpp:104
const size_t get_ntotal_face_dofs()
Total number of face DOFs over the entire mesh.
Definition HHO_LocVarDiff.hpp:118
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:179
size_t const DimPoly(int m)
const size_t DimPoly< Cell >(const int m)
Compute the size of the basis of 3-variate polynomials up to degree m.
Definition hybridcore.hpp:61
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