HArD::Core2D
Hybrid Arbitrary Degree::Core 2D - Library to implement 2D schemes with edge and cell polynomials as unknowns
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 
17 namespace HArDCore2D {
18 
19 
24 // ----------------------------------------------------------------------------
25 // Class definition
26 // ----------------------------------------------------------------------------
27 
33 class LEPNCCore : public HybridCore {
34 
35 public:
37  LEPNCCore(
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 
134 private:
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 
167 LEPNCCore::LEPNCCore(const Mesh* mesh_ptr,
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 //-----------------------------------------------------
198 std::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 
236 std::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;
282  QuadratureRule quadE = generate_quadrature_rule(*edge, 2);
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);
342  QuadratureRule quadE = generate_quadrature_rule(*edge, 1);
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 
410 inline 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 
416 inline 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 
422 inline 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 
432 const 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 
471 const 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 
586 template<typename Function>
587 Eigen::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 
644 template<typename Function>
645 Eigen::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 
678 Eigen::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 
695 double 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 
720 double 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 
739 double 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 
763 double 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 
783 Eigen::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
822 double 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
function[maxeig, mineig, cond, mat]
Definition: compute_eigs.m:1
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::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
size_t n_edges() const
Return the number of edges of the Polytope.
Definition: Polytope2D.hpp:88
Cell * cell(std::size_t index) const
get a constant pointer to a cell using its global index
Definition: Mesh2D.hpp:178
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::vector< Polytope< 1 > * > get_edges() const
Return the edges of the Polytope.
Definition: Polytope2D.hpp:83
std::size_t n_cells() const
number of cells in the mesh.
Definition: Mesh2D.hpp:63
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
Edge * edge(std::size_t index) const
get a constant pointer to a edge using its global index
Definition: Mesh2D.hpp:168
std::vector< Polytope< DIMENSION > * > get_cells() const
Return the cells of the Polytope.
Definition: Polytope2D.hpp:85
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