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