HArD::Core2D
Hybrid Arbitrary Degree::Core 2D - Library to implement 2D schemes with edge and cell polynomials as unknowns
Loading...
Searching...
No Matches
lepnccore.hpp
Go to the documentation of this file.
1// Derived class from HybridCore, provides non-conforming basis functions on generic polygonal cells
2//
3// Author: Jerome Droniou (jerome.droniou@monash.edu)
4//
5
6#ifndef LEPNCCORE_HPP
7#define LEPNCCORE_HPP
8
9#include <hybridcore.hpp>
10#include <quadraturerule.hpp>
11
17namespace HArDCore2D {
18
19
24// ----------------------------------------------------------------------------
25// Class definition
26// ----------------------------------------------------------------------------
27
33class LEPNCCore : public HybridCore {
34
35public:
38 const Mesh* mesh_ptr,
39 const size_t K,
40 const size_t L,
41 std::ostream & output = std::cout
42 );
43
45 using cell_basis_type = std::function<double(double, double)>;
46 using cell_gradient_type = std::function<VectorRd(double, double)>;
47
48
50
55 inline const cell_basis_type& nc_basis(
56 size_t iT,
57 size_t i
58 ) const;
59
61
62 inline const cell_gradient_type& nc_gradient(
63 size_t iT,
64 size_t i
65 ) const;
66
68 inline const VectorRd ml_node(
69 size_t iT,
70 size_t i
71 ) const;
72
74 const std::vector<Eigen::ArrayXd> nc_basis_quad(
75 const size_t iT,
76 const QuadratureRule quad
77 ) const;
78
80 const std::vector<Eigen::ArrayXXd> grad_nc_basis_quad(
81 const size_t iT,
82 const QuadratureRule quad
83 ) const;
84
87 Eigen::MatrixXd gram_matrix(
88 const std::vector<Eigen::ArrayXd>& f_quad,
89 const std::vector<Eigen::ArrayXd>& g_quad,
90 const size_t& nrows,
91 const size_t& ncols,
92 const QuadratureRule& quad,
93 const bool& sym,
94 std::vector<double> L2weight = {}
95 ) const;
98 Eigen::MatrixXd gram_matrix(
99 const std::vector<Eigen::ArrayXXd>& F_quad,
100 const std::vector<Eigen::ArrayXXd>& G_quad,
101 const size_t& nrows,
102 const size_t& ncols,
103 const QuadratureRule& quad,
104 const bool& sym,
105 std::vector<Eigen::Matrix2d> L2Weight = {}
106 ) const;
109 template<typename Function>
110 Eigen::VectorXd nc_interpolate_moments(const Function& f, size_t doe) const;
111
113 template<typename Function>
114 Eigen::VectorXd nc_interpolate_ml(const Function& f, size_t doe) const;
115
116 Eigen::VectorXd nc_restr(const Eigen::VectorXd &Xh, size_t iT) const;
117
118 double nc_L2norm(const Eigen::VectorXd &Xh) const;
119
120 double nc_L2norm_ml(const Eigen::VectorXd &Xh) const;
121
122 double nc_H1norm(const Eigen::VectorXd &Xh) const;
123
125 double nc_evaluate_in_cell(const Eigen::VectorXd XTF, size_t iT, double x, double y) const;
126
128 Eigen::VectorXd nc_VertexValues(
129 const Eigen::VectorXd Xh,
130 const double weight = 0
131 );
132
133
134private:
135 std::tuple<std::vector<cell_basis_type>,
136 std::vector<cell_gradient_type>,
137 Eigen::MatrixXd>
138 create_nc_basis(size_t iT) const;
139
140 std::vector<VectorRd> create_ml_nodes(size_t iT) const;
141
142 // Collections of non-conforming basis functions for each cell
143 std::vector<std::vector<cell_basis_type> > _nc_bases;
144 std::vector<std::vector<cell_gradient_type> > _nc_gradients;
145 // Basis functions to represent bubble basis functions based on monomials/edge-based functions
146 std::vector<Eigen::MatrixXd> _M_basis;
147
148 // Collection of mass-lumping nodes
149 std::vector<std::vector<VectorRd>> _ml_nodes;
150
151 // Sign function
152 double sign(const double s) const;
153
154 // Output stream
155 std::ostream & m_output;
156
157};
158
159// ************************************************************************
160// Implementation
161// ************************************************************************
162
163//-----------------------------------------------------
164//------ Create class
165//-----------------------------------------------------
166
168 const size_t K,
169 const size_t L,
170 std::ostream & output):
171 HybridCore (mesh_ptr, std::min(size_t(1),K), L, false, output),
172 _nc_bases(0),
173 _nc_gradients(0),
174 _M_basis(0),
175 _ml_nodes(0),
176 m_output(output) {
177 size_t ncells = get_mesh()->n_cells();
178 // Resize vectors
179 _nc_bases.reserve(ncells);
180 _nc_gradients.reserve(ncells);
181 _ml_nodes.reserve(ncells);
182 _M_basis.reserve(ncells);
183 // Create nodes for mass-lumping and initialise non-conforming basis functions on cells
184 for (size_t iT = 0; iT < mesh_ptr->n_cells(); iT++) {
185 std::vector<VectorRd> ml_nodes = create_ml_nodes(iT);
186 _ml_nodes.push_back(ml_nodes);
187 std::tuple<std::vector<cell_basis_type>, std::vector<cell_gradient_type>, Eigen::MatrixXd> nc_basis = create_nc_basis(iT);
188 _nc_bases.push_back(std::get<0>(nc_basis));
189 _nc_gradients.push_back(std::get<1>(nc_basis));
190 _M_basis.push_back(std::get<2>(nc_basis));
191 }
192
193}
194
195//-----------------------------------------------------
196//------ Create mass-lumping nodes
197//-----------------------------------------------------
198std::vector<VectorRd> LEPNCCore::create_ml_nodes(size_t iT) const {
199 const Mesh* mesh = get_mesh();
200 Cell* cell = mesh->cell(iT);
201 size_t nedges = cell->n_edges();
202 std::vector<VectorRd> ml_cell(mesh->dim()+1,VectorRd::Zero());
203
204 // We look for the triangle of vertices of the cell that maximises the area
205 double area_max = 0;
206 std::vector<VectorRd> v_cur(mesh->dim()+1,VectorRd::Zero());
207
208 for (size_t i=0; i < nedges; i++){
209 v_cur[0] = cell->vertex(i)->coords();
210 for (size_t j=i+1; j < nedges; j++){
211 v_cur[1] = cell->vertex(j)->coords();
212 for (size_t k=j+1; k < nedges; k++){
213 v_cur[2] = cell->vertex(k)->coords();
214 Eigen::MatrixXd matv = Eigen::MatrixXd::Zero(mesh->dim(), mesh->dim());
215 for (size_t ll=0; ll < mesh->dim(); ll++){
216 matv.col(ll) = v_cur[ll+1]-v_cur[0];
217 }
218 double area_cur = std::abs(matv.determinant());
219 if (area_cur > area_max){
220 for (size_t z=0; z < mesh->dim()+1; z++){
221 ml_cell[z] = v_cur[z];
222 }
223 area_max = area_cur;
224 }
225 }
226 }
227 }
228
229 return ml_cell;
230}
231
232//-----------------------------------------------------
233//------ Create and get basis functions and gradients
234//-----------------------------------------------------
235
236std::tuple<std::vector<LEPNCCore::cell_basis_type>,
237 std::vector<LEPNCCore::cell_gradient_type>,
238 Eigen::MatrixXd>
239 LEPNCCore::create_nc_basis(size_t iT) const {
240
241 Cell* cell = get_mesh()->cell(iT);
242 size_t nedgesT = cell->n_edges();
243 std::vector<cell_basis_type> nc_basis(DimPoly<Cell>(1)+nedgesT,[](double x, double y)->double {return 0;});
244 std::vector<cell_gradient_type> nc_gradient(nedgesT+DimPoly<Cell>(1),[](double x, double y)->VectorRd {return VectorRd::Zero();});
245
246 // Computation of basis functions: we start by the functions associated with the edges,
247 // that we put at the end of the lists
248 // For each edge, the function is constructed the following way: the triangle with the center of the cell
249 // is created, and the function is the product of the positive part of the signed distance to the
250 // edges of this triangle internal to the cell. It's therefore positive in the triangle and zero outside
251 // It is then scaled to have an average of one on the edge
252 VectorRd xT = cell->center_mass();
253 for (size_t ilE = 0; ilE < nedgesT; ilE++){
254 Edge* edge = cell->edge(ilE);
255 // Compute normals to the internal edges of the triangle
256 Eigen::Matrix2d Rot;
257 Rot << 0, 1, -1, 0;
258 std::vector<VectorRd> Nint_edge(2, VectorRd::Zero());
259 for (size_t ilV = 0; ilV < 2; ilV++){
260 // Non-normalised, non-oriented vector orthogonal to edge
261 VectorRd temp_norm = Rot * (edge->vertex(ilV)->coords() - xT);
262 // Orient and normalise to be external to the triangle
263 double orientation = sign( (xT - edge->center_mass()).dot(temp_norm) );
264 Nint_edge[ilV] = orientation * temp_norm.normalized();
265 }
266
267 // Unscaled basis function
268 double edge_length = edge->measure();
269 cell_basis_type unscaled_phi = [Nint_edge, xT, edge_length](double x, double y)->double {
270 VectorRd posX = VectorRd(x,y);
271 double val = 1;
272
273 for (size_t ilV = 0; ilV < 2; ilV++){
274 double signed_dist = (xT - posX).dot(Nint_edge[ilV]);
275 val *= std::max(double(0), signed_dist) / edge_length;
276 }
277 return val;
278 };
279
280 // Compute factor to scale basis function and get an average of one on its associated edge.
281 double scaling_factor_phi = 0;
283 for (QuadratureNode quadrule : quadE){
284 scaling_factor_phi += quadrule.w * unscaled_phi(quadrule.x, quadrule.y);
285 }
286 if (std::abs(scaling_factor_phi) < 1e-10){
287 m_output << "Scaling factor of non-conforming basis function too small: " << std::abs(scaling_factor_phi) << "\n";
288 exit(EXIT_FAILURE);
289 }
290
291 // Scale basis function
292 scaling_factor_phi /= edge->measure();
293 cell_basis_type phi = [unscaled_phi, scaling_factor_phi](double x, double y)->double {
294 return unscaled_phi(x, y) / scaling_factor_phi;
295 };
296
297 // Gradient of basis function
298 cell_gradient_type dphi = [Nint_edge, xT, scaling_factor_phi, edge_length](double x, double y)->VectorRd {
299 VectorRd val = VectorRd::Zero();
300
301 VectorRd posX = VectorRd(x,y);
302 for (size_t ilV = 0; ilV < 2; ilV++){
303 double coef_ilV = 1;
304 if ( (xT - posX).dot(Nint_edge[ilV]) < 0){
305 coef_ilV = 0;
306 }
307 for (size_t ilVp = 0; ilVp < 2; ilVp++){
308 if (ilVp != ilV){
309 double signed_dist_ilVp = (xT - posX).dot(Nint_edge[ilVp]);
310 coef_ilV *= std::max(double(0), signed_dist_ilVp) / edge_length;
311 }
312 }
313 val -= coef_ilV * Nint_edge[ilV] / edge_length;
314 }
315 return val / scaling_factor_phi;
316 };
317
318 // Store basis function and gradient
319 nc_basis[ilE+DimPoly<Cell>(1)] = std::move(phi);
320 nc_gradient[ilE+DimPoly<Cell>(1)] = std::move(dphi);
321 }
322
323 // We then put the basis functions corresponding to the first three HybridCore cell basis functions (basis of P^1)
324 // to which we subtract a combination of the previous basis functions in order to obtain functions
325 // with zero averages on all edges. In a second step, we also modify this basis function to make sure
326 // it's nodal with respect to the selected points in _ml_nodes[iT]
327 // These transformations of basis are done by computing the matrix to pass from the
328 // basis functions (basis P^1, edges basis functions) to the new basis functions.
329 Eigen::MatrixXd M_basis = Eigen::MatrixXd::Zero(DimPoly<Cell>(1), DimPoly<Cell>(1) + nedgesT);
330 M_basis.topLeftCorner(DimPoly<Cell>(1), DimPoly<Cell>(1)) = Eigen::MatrixXd::Identity(DimPoly<Cell>(1), DimPoly<Cell>(1));
331 for (size_t i = 0; i < DimPoly<Cell>(1); i++){
332 // Basis function 1, x or y, and gradient
334 cell_basis_type phi0 = [&](double x, double y)->double {
335 return CellBasis(iT).function(i,VectorRd(x,y));
336 };
337 // Averages of basis function over edges, that we store in matrix M_basis
338 // At this point, M_basis transforms the basis (1,x,y) into a basis of functions
339 // that have zero averages on the edges
340 for (size_t ilE = 0; ilE < nedgesT; ilE++){
341 Edge* edge = cell->edge(ilE);
343 for (QuadratureNode quadrule : quadE){
344 M_basis(i, DimPoly<Cell>(1)+ilE) -= quadrule.w * phi0(quadrule.x, quadrule.y);
345 }
346 M_basis(i, DimPoly<Cell>(1)+ilE) /= edge->measure();
347 }
348 }
349
350 // We then modify M_basis to ensure that the transformed basis function of P^1 are nodal with
351 // respect to _ml_nodes[iT].
352 // M_nodal_values is (phi_i(x_k))_ik where phi_i are the original basis functions and x_k
353 // are the nodal points
354 // Note that the edge basis functions all have zero value at the nodal points
355 const Mesh* mesh = get_mesh();
356 std::vector<VectorRd> nodal_points = _ml_nodes[iT];
357 Eigen::MatrixXd M_nodal_values = Eigen::MatrixXd::Zero(DimPoly<Cell>(1), mesh->dim()+1);
358 for (size_t i=0; i < DimPoly<Cell>(1); i++){
359 for (size_t k=0; k < mesh->dim() + 1; k++){
362 M_nodal_values(i, k) = CellBasis(iT).function(i, nodal_points[k]);
363 }
364 }
365 // Modification M_basis to get nodal basis functions
366 M_basis = (M_nodal_values.inverse() ) * M_basis;
367
368 // Store new basis functions replacing (1,x,y)
369 for (size_t i=0; i < DimPoly<Cell>(1); i++){
370 Eigen::VectorXd M_basis_rowi = M_basis.row(i);
371 cell_basis_type phi = [iT, nedgesT, nc_basis, M_basis_rowi, this](double x, double y)->double {
372 double val = 0;
373 for (size_t j = 0; j < DimPoly<Cell>(1)+nedgesT; j++){
374 if (j < DimPoly<Cell>(1)){
376 val += M_basis_rowi(j) * CellBasis(iT).function(j, VectorRd(x, y));
377 }else{
378 cell_basis_type phi0 = nc_basis[j];
379 val += M_basis_rowi(j) * phi0(x, y);
380 }
381 }
382 return val;
383 };
384 cell_gradient_type dphi = [iT, nedgesT, nc_gradient, M_basis_rowi, this](double x, double y)->VectorRd {
385 VectorRd val = VectorRd::Zero();
386 for (size_t j = 0; j < DimPoly<Cell>(1)+nedgesT; j++){
387 if (j < DimPoly<Cell>(1)){
389 val += M_basis_rowi(j) * CellBasis(iT).gradient(j, VectorRd(x, y));
390 }else{
392 val += M_basis_rowi(j) * dphi0(x, y);
393 }
394 }
395 return val;
396 };
397
398
399 // Store basis function and gradient
400 nc_basis[i] = std::move(phi);
401 nc_gradient[i] = std::move(dphi);
402 }
403
404 return std::make_tuple(std::move(nc_basis), std::move(nc_gradient), std::move(M_basis));
405}
406
407
408// Get basis functions, nodal points
409
410inline const LEPNCCore::cell_basis_type& LEPNCCore::nc_basis(size_t iT, size_t i) const {
411 assert(iT < this->get_mesh()->n_cells());
412 assert(i < _nc_bases[iT].size());
413 return _nc_bases[iT][i];
414}
415
416inline const LEPNCCore::cell_gradient_type& LEPNCCore::nc_gradient(size_t iT, size_t i) const {
417 assert(iT < this->get_mesh()->n_cells());
418 assert(i < _nc_gradients[iT].size());
419 return _nc_gradients[iT][i];
420}
421
422inline const VectorRd LEPNCCore::ml_node(size_t iT, size_t i) const {
423 assert(iT < this->get_mesh()->n_cells());
424 assert(i < get_mesh()->dim() + 1);
425 return _ml_nodes[iT][i];
426}
427
428// ----------------------------------------------------------------
429// ------- Non-conforming basis functions and gradients at quadrature nodes
430// ----------------------------------------------------------------
431
432const std::vector<Eigen::ArrayXd> LEPNCCore::nc_basis_quad(const size_t iT, const QuadratureRule quad) const {
433
434 size_t nbq = quad.size();
435 const Mesh* mesh = get_mesh();
436 size_t nbasis = DimPoly<Cell>(1) + mesh->cell(iT)->n_edges();
437 std::vector<Eigen::ArrayXd> nc_phi_quad(nbasis, Eigen::ArrayXd::Zero(nbq));
438
439 // Compute first on monomial basis functions and edge-based basis functions
440 std::vector<Eigen::ArrayXd> nc_phi_quad_tmp(nbasis, Eigen::ArrayXd::Zero(nbq));
441 for (size_t i = 0; i < nbasis; i++){
442 if (i < DimPoly<Cell>(1)){
444
445 for (size_t iqn = 0; iqn < nbq; iqn++){
447 nc_phi_quad_tmp[i](iqn) = CellBasis(iT).function(i, quad[iqn].vector());
448 }
449 }else{
450 const auto &nc_phi_i = nc_basis(iT, i);
451
452 for (size_t iqn = 0; iqn < nbq; iqn++){
453 nc_phi_quad_tmp[i](iqn) = nc_phi_i(quad[iqn].x, quad[iqn].y);
454 }
455 }
456 }
457
458 // Adjust for bubble cell basis functions
459 for (size_t i = DimPoly<Cell>(1); i < nbasis; i++){
460 nc_phi_quad[i] = nc_phi_quad_tmp[i];
461 }
462 for (size_t i = 0; i < DimPoly<Cell>(1); i++){
463 for (size_t j = 0; j < nbasis; j++){
464 nc_phi_quad[i] += _M_basis[iT](i,j) * nc_phi_quad_tmp[j];
465 }
466 }
467
468 return nc_phi_quad;
469}
470
471const std::vector<Eigen::ArrayXXd> LEPNCCore::grad_nc_basis_quad(const size_t iT, const QuadratureRule quad) const {
472
473 size_t nbq = quad.size();
474 const Mesh* mesh = get_mesh();
475 size_t nbasis = DimPoly<Cell>(1) + mesh->cell(iT)->n_edges();
476 std::vector<Eigen::ArrayXXd> Dnc_phi_quad(nbasis, Eigen::ArrayXXd::Zero(get_mesh()->dim(), nbq));
477
478 // Compute first on monomial basis functions and edge-based basis functions
479 std::vector<Eigen::ArrayXXd> Dnc_phi_quad_tmp(nbasis, Eigen::ArrayXXd::Zero(get_mesh()->dim(), nbq));
480 for (size_t i = 0; i < nbasis; i++){
481 if (i < DimPoly<Cell>(1)){
483
484 for (size_t iqn = 0; iqn < nbq; iqn++){
486 Dnc_phi_quad_tmp[i].col(iqn) = CellBasis(iT).gradient(i, quad[iqn].vector());
487 }
488 }else{
489 const auto &Dnc_phi_i = nc_gradient(iT, i);
490
491 for (size_t iqn = 0; iqn < nbq; iqn++){
492 Dnc_phi_quad_tmp[i].col(iqn) = Dnc_phi_i(quad[iqn].x, quad[iqn].y);
493 }
494 }
495 }
496
497 // Adjust for bubble cell basis functions
498 for (size_t i = DimPoly<Cell>(1); i < nbasis; i++){
499 Dnc_phi_quad[i] = Dnc_phi_quad_tmp[i];
500 }
501 for (size_t i = 0; i < DimPoly<Cell>(1); i++){
502 for (size_t j = 0; j < nbasis; j++){
503 Dnc_phi_quad[i] += _M_basis[iT](i,j) * Dnc_phi_quad_tmp[j];
504 }
505 }
506
507 return Dnc_phi_quad;
508}
509
510//-----------------------------------------------------
511//------ Old versions of gram matrices, to replace later perhaps
512//-----------------------------------------------------
513
514
517 Eigen::MatrixXd LEPNCCore::gram_matrix(const std::vector<Eigen::ArrayXd>& f_quad, const std::vector<Eigen::ArrayXd>& g_quad, const size_t& nrows, const size_t& ncols, const QuadratureRule& quad, const bool& sym, std::vector<double> L2weight) const {
518 Eigen::MatrixXd GGM = Eigen::MatrixXd::Zero(nrows, ncols);
519
520 size_t nbq = quad.size();
521
522 // Recast product of quadrature and L2weight into an Eigen::ArrayXd
523 Eigen::ArrayXd quad_L2_weights = Eigen::ArrayXd::Zero(nbq);
524 if (L2weight.size() == 0){
525 for (size_t iqn = 0; iqn < nbq; iqn++){
526 quad_L2_weights(iqn) = quad[iqn].w;
527 }
528 }else{
529 for (size_t iqn = 0; iqn < nbq; iqn++){
530 quad_L2_weights(iqn) = quad[iqn].w * L2weight[iqn];
531 }
532 }
533
534 for (size_t i = 0; i < nrows; i++){
535 size_t jcut = 0;
536 if (sym) jcut = i;
537 for (size_t j = 0; j < jcut; j++){
538 GGM(i, j) = GGM(j, i);
539 }
540 for (size_t j = jcut; j < ncols; j++){
541 // Integrate f_i * g_j
542 // The products here are component-wise since the terms are Eigen::ArrayXd
543 GGM(i, j) = (quad_L2_weights * f_quad[i] * g_quad[j]).sum();
544 }
545 }
546
547 return GGM;
548 }
549
551 Eigen::MatrixXd LEPNCCore::gram_matrix(const std::vector<Eigen::ArrayXXd>& F_quad, const std::vector<Eigen::ArrayXXd>& G_quad, const size_t& nrows, const size_t& ncols, const QuadratureRule& quad, const bool& sym, std::vector<Eigen::Matrix2d> L2Weight) const {
552 Eigen::MatrixXd GSM = Eigen::MatrixXd::Zero(nrows, ncols);
553
554 size_t nbq = quad.size();
555 for (size_t i = 0; i < nrows; i++){
556 size_t jcut = 0;
557 if (sym) jcut = i;
558 for (size_t j = 0; j < jcut; j++){
559 GSM(i, j) = GSM(j, i);
560 }
561 // Multiply F_i by quadrature weights and matrix L2Weight, if required
562 Eigen::ArrayXXd WeightsTimesF_quad = Eigen::ArrayXXd::Zero(get_mesh()->dim(), nbq);
563 if (L2Weight.size() == 0){
564 for (size_t iqn = 0; iqn < nbq; iqn++){
565 WeightsTimesF_quad.col(iqn) = quad[iqn].w * F_quad[i].col(iqn);
566 }
567 }else{
568 for (size_t iqn = 0; iqn < nbq; iqn++){
569 WeightsTimesF_quad.col(iqn) = quad[iqn].w * (L2Weight[iqn] * F_quad[i].col(iqn).matrix());
570 }
571 }
572 for (size_t j = jcut; j < ncols; j++){
573 // Integrate F_i * G_j
574 GSM(i, j) = (WeightsTimesF_quad * G_quad[j]).sum();
575 }
576 }
577
578
579 return GSM;
580 }
581
582//-----------------------------------------------------
583//------ Interpolate a continuous function
584//-----------------------------------------------------
585
586template<typename Function>
587Eigen::VectorXd LEPNCCore::nc_interpolate_moments(const Function& f, size_t doe) const {
588 const Mesh* mesh_ptr = get_mesh();
589 size_t ncells = mesh_ptr->n_cells();
590 size_t nedges = mesh_ptr->n_edges();
591 Eigen::VectorXd Xh = Eigen::VectorXd::Zero(DimPoly<Cell>(1)*ncells + nedges);
592
593 // Each edge components of the vector of DOFs is the integral of f over the edge
594 size_t offset_edges = ncells * DimPoly<Cell>(1);
595 for (size_t iF = 0; iF < nedges; iF++) {
596 QuadratureRule quadF = generate_quadrature_rule(*mesh_ptr->edge(iF), doe);
597 for (QuadratureNode quadrule : quadF){
598 Xh(offset_edges + iF) += quadrule.w * f(quadrule.x, quadrule.y);
599 }
600 Xh(offset_edges + iF) /= mesh_ptr->edge(iF)->measure();
601 }
602
603 // For components corresponding to the first DimPoly<Cell>(1) basis functions in each cell,
604 // we put the L2 projection of the function minus its interpolate on the other basis functions
605 // of the cell (computed using the integrals above)
606
607 for (size_t iT = 0; iT < mesh_ptr->n_cells(); iT++) {
608
609 // Mass matrix of first basis functions in cell (not the ones associated with edges)
610 QuadratureRule quadT = generate_quadrature_rule(*mesh_ptr->cell(iT), doe, true);
611 std::vector<Eigen::ArrayXd> nc_phi_quadT = nc_basis_quad(iT, quadT);
612 Eigen::MatrixXd MT = gram_matrix(nc_phi_quadT, nc_phi_quadT, DimPoly<Cell>(1), DimPoly<Cell>(1), quadT, true);
613
614 // Vector of integrals of f minus combination of functions corresponding to the edges, against basis functions
615 std::vector<Edge*> edgesT = mesh_ptr->cell(iT)->get_edges();
616 Eigen::VectorXd bT = Eigen::VectorXd::Zero(DimPoly<Cell>(1));
617 for (size_t i = 0; i < DimPoly<Cell>(1); i++) {
618 const auto& phi_i = nc_basis(iT, i);
619 std::function<double(double,double)> adjusted_f = [&f, &edgesT, &Xh, &offset_edges, &iT, this](double x, double y) {
620 double val = f(x,y);
621 for (size_t ilF = 0; ilF < edgesT.size(); ilF++){
622 size_t iF = edgesT[ilF]->global_index();
623 val -= Xh(offset_edges + iF) * nc_basis(iT, DimPoly<Cell>(1)+ilF)(x, y);
624 }
625 return val;
626 };
627 for (QuadratureNode quadrule : quadT){
628 bT(i) += quadrule.w * phi_i(quadrule.x, quadrule.y) * adjusted_f(quadrule.x, quadrule.y);
629 }
630
631 }
632
633 // Vector of coefficients (on non-conforming cell basis functions) of the L2(T) projection of f
634 Eigen::VectorXd UT = MT.ldlt().solve(bT);
635
636 // Fill in the complete vector of DOFs
637 Xh.segment(iT*DimPoly<Cell>(1), DimPoly<Cell>(1)) = UT;
638 }
639
640 return Xh;
641}
642
643
644template<typename Function>
645Eigen::VectorXd LEPNCCore::nc_interpolate_ml(const Function& f, size_t doe) const {
646 const Mesh* mesh_ptr = get_mesh();
647 size_t ncells = mesh_ptr->n_cells();
648 size_t nedges = mesh_ptr->n_edges();
649 Eigen::VectorXd Xh = Eigen::VectorXd::Zero(DimPoly<Cell>(1)*ncells + nedges);
650
651 // Each edge components of the vector of DOFs is the integral of f over the edge
652 size_t offset_edges = ncells * DimPoly<Cell>(1);
653 for (size_t iF = 0; iF < nedges; iF++) {
654 QuadratureRule quadF = generate_quadrature_rule(*mesh_ptr->edge(iF), doe);
655 for (QuadratureNode quadrule : quadF){
656 Xh(offset_edges + iF) += quadrule.w * f(quadrule.x, quadrule.y);
657 }
658 Xh(offset_edges + iF) /= mesh_ptr->edge(iF)->measure();
659 }
660
661 // For components corresponding to the first DimPoly<Cell>(1) basis functions in each cell,
662 // it's just the values at the mass-lumping nodes
663
664 for (size_t iT = 0; iT < mesh_ptr->n_cells(); iT++) {
665 for (size_t i = 0; i < DimPoly<Cell>(1); i++){
666 VectorRd node = ml_node(iT, i);
667 Xh(iT*DimPoly<Cell>(1)+i) = f(node.x(), node.y());
668 }
669 }
670
671 return Xh;
672}
673//-------------------------------------------
674//------------ Norms of discrete functions
675//-------------------------------------------
676
677
678Eigen::VectorXd LEPNCCore::nc_restr(const Eigen::VectorXd &Xh, size_t iT) const {
679
680 size_t nedgesT = get_mesh()->cell(iT)->n_edges();
681 Eigen::VectorXd XTF = Eigen::VectorXd::Zero(DimPoly<Cell>(1)+nedgesT);
682
683 XTF.head(DimPoly<Cell>(1)) = Xh.segment(iT*DimPoly<Cell>(1), DimPoly<Cell>(1));
684
685 size_t offset_edges = get_mesh()->n_cells() * DimPoly<Cell>(1);
686 for (size_t ilE = 0; ilE < nedgesT; ilE++){
687 size_t iE = get_mesh()->cell(iT)->edge(ilE)->global_index();
688 XTF(DimPoly<Cell>(1)+ilE) = Xh(offset_edges + iE);
689 }
690
691 return XTF;
692}
693
694
695double LEPNCCore::nc_L2norm(const Eigen::VectorXd &Xh) const {
696 double value = 0.0;
697
698 size_t ncells = get_mesh()->n_cells();
699
700 for (size_t iT = 0; iT < ncells; iT++) {
701 // L2 norm computed using the mass matrix
702 // Compute cell quadrature nodes and values of cell basis functions at these nodes
703 Cell* cell = get_mesh()->cell(iT);
704 size_t nedgesT = cell->n_edges();
705 size_t nlocal_dofs = DimPoly<Cell>(1)+nedgesT;
706 size_t doe = 4;
707 QuadratureRule quadT = generate_quadrature_rule(*cell, doe, true);
708 std::vector<Eigen::ArrayXd> nc_phiT_quadT = nc_basis_quad(iT, quadT);
709
710 Eigen::MatrixXd MTT = gram_matrix(nc_phiT_quadT, nc_phiT_quadT, nlocal_dofs, nlocal_dofs, quadT, true);
711 Eigen::VectorXd XTF = nc_restr(Xh, iT);
712
713 value += XTF.dot(MTT*XTF);
714
715 }
716 return sqrt(value);
717}
718
719
720double LEPNCCore::nc_L2norm_ml(const Eigen::VectorXd &Xh) const {
721 double value = 0.0;
722
723 size_t ncells = get_mesh()->n_cells();
724
725 for (size_t iT = 0; iT < ncells; iT++) {
726 Cell* cell = get_mesh()->cell(iT);
727
728 // L2 norm computed using mass-lumped version, so only based on first DimPoly<Cell>(1) basis
729 // functions, with very simple coefficients
730 for (size_t i = 0; i < DimPoly<Cell>(1); i++){
731 value += (cell->measure()/DimPoly<Cell>(1)) * std::pow(Xh(iT*DimPoly<Cell>(1)+i), 2);
732 }
733 }
734
735 return sqrt(value);
736}
737
738
739double LEPNCCore::nc_H1norm(const Eigen::VectorXd &Xh) const {
740 double value = 0.0;
741
742 size_t ncells = get_mesh()->n_cells();
743
744 for (size_t iT = 0; iT < ncells; iT++) {
745 Cell* cell = get_mesh()->cell(iT);
746 size_t nlocal_dofs = DimPoly<Cell>(1) + cell->n_edges();
747 size_t doe = 2;
748 QuadratureRule quadT = generate_quadrature_rule(*cell, doe, true);
749 std::vector<Eigen::ArrayXXd> Dnc_phiT_quad = grad_nc_basis_quad(iT, quadT);
750 Eigen::MatrixXd StiffT = gram_matrix(Dnc_phiT_quad, Dnc_phiT_quad, nlocal_dofs, nlocal_dofs, quadT, true);
751 Eigen::VectorXd XTF = nc_restr(Xh, iT);
752 value += XTF.dot(StiffT * XTF);
753
754 }
755 return sqrt(value);
756}
757
758
759// ----------------------------------------------------------------
760// ------- Compute vertex values from a non-conforming function
761// ----------------------------------------------------------------
762
763double LEPNCCore::nc_evaluate_in_cell(const Eigen::VectorXd Xh, size_t iT, double x, double y) const {
764 double value = 0.0;
765 const Mesh* mesh_ptr = get_mesh();
766 for (size_t i = 0; i < DimPoly<Cell>(1); i++) {
767 const auto &phi_i = nc_basis(iT, i);
768 const size_t index = iT * DimPoly<Cell>(1) + i;
769 value += Xh(index) * phi_i(x,y);
770 }
771
772 size_t nedgesT = mesh_ptr->cell(iT)->n_edges();
773 size_t offset_edges = mesh_ptr->n_cells() * DimPoly<Cell>(1);
774 for (size_t ilE = 0; ilE < nedgesT; ilE++) {
775 const auto &phi_i = nc_basis(iT, DimPoly<Cell>(1) + ilE);
776 const size_t index = offset_edges + mesh_ptr->cell(iT)->edge(ilE)->global_index();
777 value += Xh(index) * phi_i(x,y);
778 }
779
780 return value;
781}
782
783Eigen::VectorXd LEPNCCore::nc_VertexValues(const Eigen::VectorXd Xh, const double weight) {
784 const Mesh* mesh_ptr = get_mesh();
785
786 // Average of values, at the vertices, given by basis functions associated with the edges around each vertex
787 Eigen::VectorXd function_vertex_cells = Eigen::VectorXd::Zero(mesh_ptr->n_vertices());
788 for (size_t iV = 0; iV < mesh_ptr->n_vertices(); iV++){
789 auto xV = mesh_ptr->vertex(iV)->coords();
790 auto cList = mesh_ptr->vertex(iV)->get_cells();
791 auto nb_cells = cList.size();
792 for (size_t ilT = 0; ilT < nb_cells; ilT++){
793 auto temp = nc_evaluate_in_cell(Xh, cList[ilT]->global_index(), xV.x(), xV.y());
794 function_vertex_cells(iV) += temp;
795 }
796 function_vertex_cells(iV) /= nb_cells;
797 }
798
799 // Average of values, at the vertices, given by basis functions associated with the edges, considered at the edge
800 // midpoints (they vanish on the vertex)
801 Eigen::VectorXd function_vertex_edges = Eigen::VectorXd::Zero(mesh_ptr->n_vertices());
802 for (size_t iV = 0; iV < mesh_ptr->n_vertices(); iV++){
803 auto eList = mesh_ptr->vertex(iV)->get_edges();
804 auto nb_edges = eList.size();
805 for (Edge* edge : eList){
806 auto cList_edge = edge->get_cells();
807 VectorRd xE = edge->center_mass();
808 // We average the values considered from both cells around the edge
809 auto nb_cells_edges = cList_edge.size();
810 for (Cell* cell : cList_edge){
811 function_vertex_edges(iV) += nc_evaluate_in_cell(Xh, cell->global_index(), xE.x(), xE.y()) / nb_cells_edges;
812 }
813 }
814 function_vertex_edges(iV) /= nb_edges;
815 }
816
817 return (1-weight)*function_vertex_cells + weight*function_vertex_edges;
818}
819
820
821// Sign function
822double LEPNCCore::sign(const double s) const {
823 double val = 0;
824 if (s>0){
825 val = 1;
826 }else if (s<0){
827 val = -1;
828 }
829
830 return val;
831}
832
834
835} // end of namespace HArDCore2D
836
837#endif /* LEPNCCORE_HPP */
Definition hybridcore.hpp:179
Definition lepnccore.hpp:33
Definition Mesh2D.hpp:26
Compute max and min eigenvalues of all matrices for i
Definition compute_eigs.m:5
GradientValue gradient(size_t i, const VectorRd &x) const
Evaluate the gradient of the i-th function at point x.
Definition basis.hpp:378
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
@ Function
Definition basis.hpp:1673
size_t L
Definition HHO_DiffAdvecReac.hpp:46
size_t K
Definition HHO_DiffAdvecReac.hpp:46
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 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
Eigen::VectorXd nc_VertexValues(const Eigen::VectorXd Xh, const double weight=0)
From a non-conforming discrete function, computes a vector of values at the vertices of the mesh.
Definition lepnccore.hpp:783
Eigen::MatrixXd gram_matrix(const std::vector< Eigen::ArrayXd > &f_quad, const std::vector< Eigen::ArrayXd > &g_quad, const size_t &nrows, const size_t &ncols, const QuadratureRule &quad, const bool &sym, std::vector< double > L2weight={}) const
Definition lepnccore.hpp:517
double nc_L2norm(const Eigen::VectorXd &Xh) const
Compute L2 norm of a discrete function (given by coefficients on the basis functions)
Definition lepnccore.hpp:695
LEPNCCore(const Mesh *mesh_ptr, const size_t K, const size_t L, std::ostream &output=std::cout)
Class constructor: initialise the LEPNCCore class, and creates non-conforming basis functions (and gr...
Definition lepnccore.hpp:167
const std::vector< Eigen::ArrayXd > nc_basis_quad(const size_t iT, const QuadratureRule quad) const
Computes non-conforming basis functions at the given quadrature nodes.
Definition lepnccore.hpp:432
Eigen::VectorXd nc_restr(const Eigen::VectorXd &Xh, size_t iT) const
Extract from a global vector Xh of unknowns the non-conforming unknowns corresponding to cell iT.
Definition lepnccore.hpp:678
const std::vector< Eigen::ArrayXXd > grad_nc_basis_quad(const size_t iT, const QuadratureRule quad) const
Compute at the quadrature nodes, where are the cell basis functions.
Definition lepnccore.hpp:471
std::function< VectorRd(double, double)> cell_gradient_type
type for gradients of cell basis
Definition lepnccore.hpp:46
const cell_gradient_type & nc_gradient(size_t iT, size_t i) const
Return a reference to the gradient of the i'th non-conforming basis function of the cell iT.
Definition lepnccore.hpp:416
double nc_evaluate_in_cell(const Eigen::VectorXd XTF, size_t iT, double x, double y) const
Evaluates a non-conforming discrete function in the cell iT at point (x,y)
Definition lepnccore.hpp:763
std::function< double(double, double)> cell_basis_type
type for cell basis
Definition lepnccore.hpp:45
double nc_L2norm_ml(const Eigen::VectorXd &Xh) const
Compute L2 norm of the mass-lumped discrete function (given by coefficients on the basis functions)
Definition lepnccore.hpp:720
const VectorRd ml_node(size_t iT, size_t i) const
Return the i'th nodal point in cell iT (for mass lumping)
Definition lepnccore.hpp:422
Eigen::VectorXd nc_interpolate_ml(const Function &f, size_t doe) const
Interpolates a continuous function on the degrees of freedom, using the moments on the basis function...
Definition lepnccore.hpp:645
Eigen::VectorXd nc_interpolate_moments(const Function &f, size_t doe) const
Interpolates a continuous function on the degrees of freedom, using the moments on the basis function...
Definition lepnccore.hpp:587
const cell_basis_type & nc_basis(size_t iT, size_t i) const
Return a reference to the i'th non-conforming basis function of the cell iT.
Definition lepnccore.hpp:410
double nc_H1norm(const Eigen::VectorXd &Xh) const
Compute broken H1 norm of a discrete function (given by coefficients on the basis functions)
Definition lepnccore.hpp:739
double measure() const
Return the Lebesgue measure of the Polytope.
Definition Polytope2D.hpp:76
Polytope< DIMENSION > Cell
Definition Polytope2D.hpp:151
std::vector< Polytope< DIMENSION > * > get_cells() const
Return the cells of the Polytope.
Definition Polytope2D.hpp:85
std::size_t n_vertices() const
number of vertices in the mesh.
Definition Mesh2D.hpp:60
std::size_t dim() const
dimension of the mesh
Definition Mesh2D.hpp:58
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
std::vector< Polytope< 1 > * > get_edges() const
Return the edges of the Polytope.
Definition Polytope2D.hpp:83
Vertex * vertex(std::size_t index) const
get a constant pointer to a vertex using its global index
Definition Mesh2D.hpp:163
std::size_t n_edges() const
number of edges in the mesh.
Definition Mesh2D.hpp:61
VectorRd coords() const
Return the coordinates of a Vertex.
Definition Polytope2D.hpp:371
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
for j
Definition mmread.m:174
Definition ddr-klplate.hpp:27
Description of one node and one weight from a quadrature rule.
Definition quadraturerule.hpp:41