HArD::Core2D
Hybrid Arbitrary Degree::Core 2D - Library to implement 2D schemes with edge and cell polynomials as unknowns
LEPNC_StefanPME_transient.hpp
Go to the documentation of this file.
1 // Implementation of the NC Poly scheme in 2D for the transient Stefan and PME models
2 //
3 // { d_t u - div(K \grad(zeta(u))) = f, inside Omega
4 // { K \grad(zeta(u)) . nTF = g, on GammaN
5 // { u = g, on GammaD
6 // { u(0) = u_ini
7 //
8 // At the moment, only pure Neumann or pure Dirichlet
9 //
10 // Author: Jerome Droniou (jerome.droniou@monash.edu)
11 //
12 
13 #ifndef _LEPNC_STEFANPME_TRANSIENT_HPP
14 #define _LEPNC_STEFANPME_TRANSIENT_HPP
15 
16 #include <functional>
17 #include <utility>
18 #include <iostream>
19 
20 #include <boost/timer/timer.hpp>
21 
22 // Matrices and linear solvers
23 #include <Eigen/Sparse>
24 #include <Eigen/Dense>
25 //#include "Eigen/MA41.cpp"
26 
27 #include "mesh.hpp"
28 #include "lepnccore.hpp"
29 #include "quad2d.hpp"
33 
38 namespace HArDCore2D {
39 
44 // ----------------------------------------------------------------------------
45 // Class definition
46 // ----------------------------------------------------------------------------
47 /* The LEPNC_StefanPME_Transient class provides an implementation of the LEPNC method for the transient Stefan and PME problems.
48 
49 * If using this code in a scientific publication, please cite the reference for the LEPNC:
50 *
51 * Non-conforming finite elements on polytopal mesh,
52 * J. Droniou, R. Eymard, T. Gallouët and R. Herbin
53 * url:
54 *
55 */
57 
59 
61 
62 // Types
63 public:
64  using solution_function_type = std::function<double(const double&, const VectorRd&)>;
65  using source_function_type = std::function<double(const double&, const VectorRd&, const Cell*)>;
66  using grad_function_type = std::function<VectorRd(const double&, const VectorRd&, const Cell*)>;
67  using tensor_function_type = std::function<Eigen::Matrix2d(const double, const double, const Cell*)>;
68 
71  LEPNCCore &nc,
72  tensor_function_type kappa,
73  size_t deg_kappa,
74  source_function_type source,
76  solution_function_type exact_solution,
77  grad_function_type grad_exact_solution,
79  double weight,
80  std::string solver_type,
81  std::ostream & output = std::cout
82  );
83 
85  Eigen::VectorXd iterate(
86  const double tps,
87  const double dt,
88  const Eigen::VectorXd Xn
89  );
90 
92  Eigen::VectorXd apply_nonlinearity(const Eigen::VectorXd& Y, const std::string type) const;
93 
95  double L2_MassLumped(const Eigen::VectorXd Xh) const;
96 
98  double Lp_MassLumped(const Eigen::VectorXd Xh, double p) const;
99 
101  double EnergyNorm(const Eigen::VectorXd Xh) const;
102 
104  inline double get_assembly_time() const {
105  return double(_assembly_time) * pow(10, -9);
106  }
108  inline double get_solving_time() const {
109  return double(_solving_time) * pow(10, -9);
110  }
112  inline double get_itime(size_t idx) const {
113  return double(_itime[idx]) * pow(10, -9);
114  }
116  inline double get_solving_error() const {
117  return _solving_error;
118  }
120  inline size_t get_nb_newton() const {
121  return m_nb_newton;
122  }
124  inline Eigen::MatrixXd get_MassT(size_t iT) const {
125  return MassT[iT];
126  }
127 
128 
129 private:
131  Eigen::MatrixXd diffusion_operator(const size_t iT) const;
132 
134  Eigen::VectorXd load_operator_ml(const double tps, const size_t iT) const;
135 
138  Eigen::VectorXd residual_scheme(const double dt, const Eigen::VectorXd& Xh) const;
139 
140  const LEPNCCore& nc;
141  const tensor_function_type kappa;
142  size_t _deg_kappa;
143  const source_function_type source;
144  const BoundaryConditions m_BC;
145  const solution_function_type exact_solution;
146  const grad_function_type grad_exact_solution;
148  const double _weight;
149  const std::string solver_type;
150  std::ostream & m_output;
151 
152  // To store local diffusion, mass and source terms
153  std::vector<Eigen::MatrixXd> DiffT;
154  std::vector<Eigen::MatrixXd> MassT;
155  std::vector<Eigen::VectorXd> RightHandSideT;
156 
157  // Computation statistics
158  size_t _assembly_time;
159  size_t _solving_time;
160  double _solving_error;
161  mutable std::vector<size_t> _itime = std::vector<size_t>(10, 0);
162  size_t m_nb_newton;
163 
164 };
165 
166 LEPNC_StefanPME_Transient::LEPNC_StefanPME_Transient(LEPNCCore &nc, tensor_function_type kappa, size_t deg_kappa, source_function_type source, BoundaryConditions BC, solution_function_type exact_solution, grad_function_type grad_exact_solution, TestCaseNonLinearity::nonlinearity_function_type zeta, double weight, std::string solver_type, std::ostream & output)
167  : nc(nc),
168  kappa(kappa),
169  _deg_kappa(deg_kappa),
170  source(source),
171  m_BC(BC),
172  exact_solution(exact_solution),
173  grad_exact_solution(grad_exact_solution),
174  zeta(zeta),
175  _weight(weight),
176  solver_type(solver_type),
177  m_output(output) {
178 
179  m_output << "[LEPNC_StefanPME_Transient] Initializing" << std::endl;
180  //---- Compute diffusion and mass matrix (do not change during time stepping or Newton) ------//
181  const Mesh* mesh = nc.get_mesh();
182  DiffT.resize(mesh->n_cells());
183  MassT.resize(mesh->n_cells());
184  // Source is pre-allocated but not computed as it changes with each time iteration
185  RightHandSideT.resize(mesh->n_cells());
186  for (size_t iT = 0; iT < mesh->n_cells(); iT++) {
187  size_t n_edgesT = mesh->cell(iT)->n_edges();
188  size_t n_local_dofs = DimPoly<Cell>(1) + n_edgesT;
189  double measT = mesh->cell(iT)->measure();
190  DiffT[iT] = diffusion_operator(iT);
191  MassT[iT] = Eigen::MatrixXd::Zero(n_local_dofs, n_local_dofs);
192  MassT[iT].topLeftCorner(DimPoly<Cell>(1), DimPoly<Cell>(1)) = (1-_weight) * (measT / DimPoly<Cell>(1)) * Eigen::MatrixXd::Identity(DimPoly<Cell>(1), DimPoly<Cell>(1));
193  MassT[iT].bottomRightCorner(n_edgesT, n_edgesT) = _weight * (measT / n_edgesT) * Eigen::MatrixXd::Identity(n_edgesT, n_edgesT);
194  }
195 }
196 
197 // PERFORM ONE TIME ITERATION
198 Eigen::VectorXd LEPNC_StefanPME_Transient::iterate(const double tps, const double dt, const Eigen::VectorXd Xn) {
199 
200  boost::timer::cpu_timer timer;
201  boost::timer::cpu_timer timerint;
202  const auto mesh = nc.get_mesh();
203  size_t n_edges_dofs = mesh->n_edges();
204  size_t n_cell_dofs = mesh->n_cells() * DimPoly<Cell>(1);
205 
206  //----- COMPUTE SOURCE: load + previous time -----//
207  for (size_t iT = 0; iT < mesh->n_cells(); iT++) {
208  RightHandSideT[iT] = dt * load_operator_ml(tps, iT) + MassT[iT]*nc.nc_restr(Xn, iT);
209  }
210 
211  //----- PARAMETERS FOR NEWTON ------------//
212  constexpr double tol = 1e-8;
213  constexpr size_t maxiter = 400;
214  // relaxation parameter
215  double relax = 1;
216  size_t iter = 0;
217  Eigen::VectorXd Xhprev = Eigen::VectorXd::Zero(n_cell_dofs + n_edges_dofs);
218  Eigen::VectorXd Xh = Eigen::VectorXd::Zero(n_cell_dofs + n_edges_dofs);
219 
220  // Vector of Dirichlet BC, either on u (if weight>0) or zeta(u) (if weight=0)
221  Eigen::VectorXd DirBC = Eigen::VectorXd::Zero(n_cell_dofs + n_edges_dofs);
222  for (size_t ibF = 0; ibF < mesh->n_b_edges(); ibF++){
223  Edge* edge = mesh->b_edge(ibF);
224  if (m_BC.type(*edge)=="dir"){
225  size_t iF = edge->global_index();
226  QuadratureRule quadF = generate_quadrature_rule(*edge, 5);
227  for (QuadratureNode quadrule : quadF){
228  if (_weight == 0){
229  DirBC(n_cell_dofs + iF) += quadrule.w * zeta(exact_solution(tps, quadrule.vector()), "fct");
230  }else{
231  DirBC(n_cell_dofs + iF) += quadrule.w * exact_solution(tps, quadrule.vector());
232  }
233  }
234  // Take average
235  DirBC(n_cell_dofs + iF) /= mesh->b_edge(ibF)->measure();
236  }
237  }
238  Xhprev = DirBC;
239 
240 
241  // ---- NEWTON ITERATONS ------ //
242  Eigen::VectorXd RES = residual_scheme(dt, Xhprev); // Scheme is F(X)=C, then RES = C - F(u_prev)
243  std::vector<double> residual(maxiter, 1.0);
244  residual[iter] = RES.norm();
245 
246  while ( (iter < maxiter) && (residual[iter] > tol) ){
247  iter++;
248 
249  // System matrix and initialisation of RHS (only on the edge dofs)
250  Eigen::SparseMatrix<double> GlobMat(n_edges_dofs, n_edges_dofs);
251  std::vector<Eigen::Triplet<double>> triplets_GlobMat;
252  Eigen::VectorXd GlobRHS = RES.tail(n_edges_dofs);
253 
254  // Static condensation: matrix and source to recover cell unknowns
255  Eigen::SparseMatrix<double> ScMat(n_cell_dofs, n_edges_dofs);
256  std::vector<Eigen::Triplet<double>> triplets_ScMat;
257  Eigen::VectorXd ScRHS = Eigen::VectorXd::Zero(n_cell_dofs);
258 
259  // Assemble local contributions
260  for (size_t iT = 0; iT < mesh->n_cells(); iT++) {
261  Cell* cell = mesh->cell(iT);
262  size_t n_edgesT = cell->n_edges();
263 
264  // Local static condensation of element unknowns
265  Eigen::VectorXd XTprev = nc.nc_restr(Xhprev, iT);
266  Eigen::VectorXd ZetaprimeXTprev = apply_nonlinearity(XTprev, "der");
267  Eigen::MatrixXd MatZetaprimeXTprev = Eigen::MatrixXd::Zero(DimPoly<Cell>(1)+n_edgesT, DimPoly<Cell>(1)+n_edgesT);
268  for (size_t i = 0; i < DimPoly<Cell>(1) + n_edgesT; i++){
269  MatZetaprimeXTprev(i, i) = ZetaprimeXTprev(i);
270  }
271  Eigen::MatrixXd MatT = dt * DiffT[iT] * MatZetaprimeXTprev + MassT[iT];
272  Eigen::MatrixXd ATT = MatT.topLeftCorner(DimPoly<Cell>(1), DimPoly<Cell>(1));
273  Eigen::MatrixXd ATF = MatT.topRightCorner(DimPoly<Cell>(1), n_edgesT);
274  Eigen::MatrixXd AFT = MatT.bottomLeftCorner(n_edgesT, DimPoly<Cell>(1));
275  Eigen::MatrixXd AFF = MatT.bottomRightCorner(n_edgesT, n_edgesT);
276 
277  Eigen::PartialPivLU<Eigen::MatrixXd> invATT;
278  invATT.compute(ATT);
279 
280  Eigen::MatrixXd invATT_ATF = invATT.solve(ATF);
281  Eigen::VectorXd RES_cell = RES.segment(iT * DimPoly<Cell>(1), DimPoly<Cell>(1));
282  Eigen::VectorXd invATT_RES_cell = invATT.solve(RES_cell);
283 
284  // Local matrix and right-hand side on the face unknowns
285  Eigen::MatrixXd MatF = Eigen::MatrixXd::Zero(n_edgesT, n_edgesT);
286  Eigen::VectorXd bF = Eigen::VectorXd::Zero(n_edgesT);
287  MatF = AFF - AFT * invATT_ATF;
288  bF = - AFT * invATT_RES_cell; // only additional RHS coming from static condensation, beyond RES
289 
290  // Assemble static condensation operator
291  ScRHS.segment(iT * DimPoly<Cell>(1), DimPoly<Cell>(1)) = invATT_RES_cell;
292  for (size_t i = 0; i < DimPoly<Cell>(1); i++){
293  size_t iGlobal = iT * DimPoly<Cell>(1) + i;
294  for (size_t j = 0; j < n_edgesT; j++){
295  size_t jGlobal = cell->edge(j)->global_index();
296  triplets_ScMat.emplace_back(iGlobal, jGlobal, invATT_ATF(i, j));
297  }
298  }
299 
300  // GLOBAL MATRIX and RHS on the edge unknowns, using the matrices MatF obtained after static condensation
301  for (size_t i = 0; i < n_edgesT; i++){
302  size_t iGlobal = cell->edge(i)->global_index();
303  for (size_t j = 0; j < n_edgesT; j++){
304  size_t jGlobal = cell->edge(j)->global_index();
305  triplets_GlobMat.emplace_back(iGlobal, jGlobal, MatF(i, j));
306  }
307  GlobRHS(iGlobal) += bF(i);
308  }
309  }
310 
311  // Assemble the global linear system (without BC), and matrix to recover statically-condensed cell dofs
312  GlobMat.setFromTriplets(std::begin(triplets_GlobMat), std::end(triplets_GlobMat));
313  ScMat.setFromTriplets(std::begin(triplets_ScMat), std::end(triplets_ScMat));
314 
315  // Dirichlet boundary conditions are trivial. Newton requires to solve
316  // F'(X^k) (X^{k+1} - X^k) = B - F(X^k)
317  // Both X^k and X^{k+1} have fixed Dirichlet values on boundary edges, so the system we solve
318  // is Dirichlet homogeneous, and we just select the non-dirichlet edges (first ones in the list)
319  size_t n_edge_unknowns = mesh->n_edges() - m_BC.n_dir_edges();
320  Eigen::SparseMatrix<double> A = GlobMat.topLeftCorner(n_edge_unknowns, n_edge_unknowns);
321  Eigen::VectorXd B = GlobRHS.head(n_edge_unknowns);
322 
323  // Solve condensed system and recover cell unknowns. dX = X^{k+1}-X^k//
324  Eigen::BiCGSTAB<Eigen::SparseMatrix<double> > solver;
325  solver.compute(A);
326  Eigen::VectorXd dX_edge_unknowns = solver.solve(B);
327 
328  Eigen::VectorXd dX = Eigen::VectorXd::Zero(n_cell_dofs + n_edges_dofs);
329  dX.segment(n_cell_dofs, n_edge_unknowns) = dX_edge_unknowns;
330  dX.head(n_cell_dofs) = ScRHS - ScMat * dX.tail(n_edges_dofs);
331 
332  // Recover the fixed boundary values and cell unknowns (from static condensation and Newton).
333  // We start by putting the Dirichlet BC at the end.
334  Xh = DirBC;
335  Xh.head(n_cell_dofs + n_edge_unknowns) = relax*dX.head(n_cell_dofs + n_edge_unknowns) + Xhprev.head(n_cell_dofs + n_edge_unknowns);
336 
337  // Compute new residual. We might not accept new Xh but start over reducing relax. Otherwise, accept Xh and increase relax
338  // to a max of 1
339  Eigen::VectorXd RES_temp = residual_scheme(dt, Xh);
340  double residual_temp = RES_temp.norm();
341  if (iter > 1 && residual_temp > 10*residual[iter-1]){
342  // We don't update the residual and Xh, but we will do another iteration with relax reduced
343  relax = relax/2.0;
344  iter--;
345  } else {
346  // Increase relax
347  relax = std::min(1.0, 1.2*relax);
348  // Store Xh in Xhprev, compute new residual
349  Xhprev = Xh;
350  RES = RES_temp;
351  residual[iter] = residual_temp;
352  }
353 
354  m_output << " ...Iteration: " << iter << "; residual = " << residual[iter] << "; relax = "<< relax << "\n";
355 
356  } //-------- END NEWTON ITERATIONS -----------//
357  m_nb_newton = iter;
358 
359  return Xh;
360 }
361 
362 //****************************************
363 // apply nonlinearity zeta to a vector
364 //****************************************
365 
366 Eigen::VectorXd LEPNC_StefanPME_Transient::apply_nonlinearity(const Eigen::VectorXd& Y, const std::string type) const {
367  // Type="fct", "nlin" or "der" as per the nonlinear function zeta
368  // The function applied to each coefficient of Y depends on its nature. If it's a coefficient appearing in
369  // the mass-lumping (which depends on _weight), the unknown represents u and we apply the full nonlinear function zeta.
370  // Otherwise, the unknown is zeta(u) itself and the function we apply is the identity s->s (with nlin/der: s-> 1)
371  //
372 
373  Eigen::VectorXd ZetaY;
374  // Initialisation depends on "type". We initialise as if the nonlinearity was the identity, we'll change the
375  // appropriate coefficients afterwards
376  if (type == "fct"){
377  ZetaY = Y;
378  }else if (type == "der" || type == "nlin"){
379  ZetaY = Eigen::VectorXd::Ones(Y.size());
380  }else{
381  m_output << "type unknown in apply_nonlinearity: " << type << "\n";
382  exit(EXIT_FAILURE);
383  }
384 
385  const Mesh* mesh = nc.get_mesh();
386  // We check if Y is a local vector or a global one, this determines the number of cell and edge unknowns
387  size_t n_cell_dofs = 0;
388  if (size_t(Y.size()) == DimPoly<Cell>(1)*mesh->n_cells() + mesh->n_edges()){
389  n_cell_dofs = DimPoly<Cell>(1)*mesh->n_cells();
390  }else{
391  n_cell_dofs = DimPoly<Cell>(1);
392  }
393  size_t n_edges_dofs = Y.size() - n_cell_dofs;
394 
395  // The type of nonlinearity we apply on each coefficient depends on _weight
396  if (_weight == 0){
397  for (size_t i = 0; i < n_cell_dofs; i++){
398  ZetaY(i) = zeta(Y(i), type);
399  }
400  }else if (_weight == 1){
401  for (size_t i = n_cell_dofs; i < n_cell_dofs + n_edges_dofs; i++){
402  ZetaY(i) = zeta(Y(i), type);
403  }
404  }else{
405  for (size_t i = 0; i < n_cell_dofs + n_edges_dofs; i++){
406  ZetaY(i) = zeta(Y(i), type);
407  }
408  }
409 
410 
411  return ZetaY;
412 }
413 
414 
415 //********************************
416 // local diffusion matrix
417 //********************************
418 
419 Eigen::MatrixXd LEPNC_StefanPME_Transient::diffusion_operator(const size_t iT) const {
420 
421  const auto mesh = nc.get_mesh();
422  Cell* cell = mesh->cell(iT);
423  const size_t n_edgesT = cell->n_edges();
424 
425  // Total number of degrees of freedom local to this cell
426  size_t local_dofs = n_edgesT + DimPoly<Cell>(1);
427 
428  // Diffusion in the cell.
429  std::function<Eigen::Matrix2d(double,double)> kappaT = [&](double x, double y){
430  return kappa(x, y, cell);
431  };
432 
433  // Compute cell quadrature nodes and values of cell basis functions and gradients at these nodes
434  size_t doe = 2 + _deg_kappa;
435  QuadratureRule quadT = generate_quadrature_rule(*cell, doe, true);
436  std::vector<Eigen::ArrayXXd> Dnc_phiT_quadT = nc.grad_nc_basis_quad(iT, quadT);
437  // Diffusion tensor at the quadrature nodes
438  std::vector<Eigen::Matrix2d> kappaT_quadT(quadT.size());
439  std::transform(quadT.begin(), quadT.end(), kappaT_quadT.begin(),
440  [&kappaT](QuadratureNode qr) -> Eigen::MatrixXd { return kappaT(qr.x, qr.y); });
441 
442  return nc.gram_matrix(Dnc_phiT_quadT, Dnc_phiT_quadT, local_dofs, local_dofs, quadT, true, kappaT_quadT);
443 }
444 
445 
446 //********************************
447 // local load term
448 //********************************
449 
450 Eigen::VectorXd LEPNC_StefanPME_Transient::load_operator_ml(const double tps, const size_t iT) const {
451  // Load for the cell DOFs (first indices) and face DOFs (last indices)
452  const auto mesh = nc.get_mesh();
453  Cell* cell = mesh->cell(iT);
454  size_t n_edgesT = cell->n_edges();
455  size_t local_dofs = DimPoly<Cell>(1) + n_edgesT;
456 
457  // Beware, MassT[iT] must have been created
458  if (MassT.size() < iT || size_t(MassT[iT].rows()) != local_dofs){
459  m_output << "Called load_operator_ml without creating MassT[iT]\n";
460  exit(EXIT_FAILURE);
461  }
462  Eigen::VectorXd fvec = Eigen::VectorXd::Zero(DimPoly<Cell>(1) + n_edgesT);
463  for (size_t i = 0; i < local_dofs; i++){
464  VectorRd node;
465  if (i < DimPoly<Cell>(1)){
466  node = nc.ml_node(iT, i);
467  }else{
468  node = cell->edge(i-DimPoly<Cell>(1))->center_mass();
469  }
470  fvec(i) = source(tps, node, cell);
471  }
472  // Contribution of source to loading term
473  Eigen::VectorXd load = MassT[iT] * fvec;
474 
475  // Adding Neumann boundary conditions
476  // The boundary conditions coming from Neumann edges are computed based on the piecewise constant
477  // discrete trace: if g is the value of the outer flux,
478  // \int g v -> \int g Tv
479  // where (Tv)_e = average of v on e, for e Neumann edge.
480  // Only edge-based basis functions therefore have a contribution, which will simply be \int_e g.
481  if (cell->is_boundary()){
482  for (size_t ilF = 0; ilF < cell->n_edges(); ilF++) {
483  Edge* edge = cell->edge(ilF);
484  if (m_BC.type(*edge)=="neu"){
485  if (edge->is_boundary()){
486  const auto& nTF = cell->edge_normal(ilF);
487  QuadratureRule quadF = generate_quadrature_rule(*edge, 5);
488  std::function<double(VectorRd)> Kgrad_n = [&](VectorRd p){
489  return zeta(exact_solution(tps, p), "der") * nTF.dot(kappa(p.x(),p.y(),cell) * grad_exact_solution(tps, p,cell));
490  };
491  for (QuadratureNode qr : quadF){
492  load(DimPoly<Cell>(1) + ilF) += qr.w * Kgrad_n(qr.vector());
493  }
494  }
495  }
496  }
497  }
498 
499  return load;
500 }
501 
502 //****************************
503 // Residual non-linear scheme
504 //****************************
505 
506 Eigen::VectorXd LEPNC_StefanPME_Transient::residual_scheme(const double dt, const Eigen::VectorXd& Xh) const{
507 
508  const Mesh* mesh = nc.get_mesh();
509  Eigen::VectorXd RES = Eigen::VectorXd::Zero(mesh->n_cells() * DimPoly<Cell>(1) + mesh->n_edges());
510 
511  // Compute the residual: C - (dt * DIFF * ZetaXh + MASS * Xh), where DIFF is the diffusion matrix, MASS is the mass matrix and C is the source term
512  size_t n_cell_dofs = mesh->n_cells() * DimPoly<Cell>(1);
513  for (size_t iT = 0; iT < mesh->n_cells(); iT++){
514  Cell* cell = mesh->cell(iT);
515  size_t n_edgesT = cell->n_edges();
516  // Local unknown in the cell, and zeta of these unknowns
517  Eigen::VectorXd XT = nc.nc_restr(Xh, iT);
518  Eigen::VectorXd ZetaXT = apply_nonlinearity(XT, "fct");
519 
520  Eigen::VectorXd REST = RightHandSideT[iT] - (dt * DiffT[iT] * ZetaXT + MassT[iT] * XT);
521  RES.segment(iT*DimPoly<Cell>(1), DimPoly<Cell>(1)) += REST.head(DimPoly<Cell>(1));
522  for (size_t ilE = 0; ilE < n_edgesT; ilE++){
523  size_t iE = cell->edge(ilE)->global_index();
524  RES(n_cell_dofs + iE) += REST(DimPoly<Cell>(1)+ilE);
525  }
526  }
527 
528  // No residual on Dirichlet edges (do not correspond to equations of the system)
529  RES.tail(m_BC.n_dir_edges()) = Eigen::VectorXd::Zero(m_BC.n_dir_edges());
530 
531  return RES;
532 }
533 
534 //*******************************************************
535 // Norms: L2 mass lumped, Energy (pure diffusion)
536 //*******************************************************
537 
538 double LEPNC_StefanPME_Transient::L2_MassLumped(const Eigen::VectorXd Xh) const {
539  const Mesh* mesh = nc.get_mesh();
540  double value = 0.0;
541 
542  for (size_t iT = 0; iT < mesh-> n_cells(); iT++) {
543  Eigen::VectorXd XTF = nc.nc_restr(Xh, iT);
544  value += XTF.transpose() * MassT[iT] * XTF;
545  }
546 
547  return sqrt(value);
548 }
549 
550 
551 double LEPNC_StefanPME_Transient::Lp_MassLumped(const Eigen::VectorXd Xh, const double p) const {
552  const Mesh* mesh = nc.get_mesh();
553  double value = 0.0;
554 
555  for (size_t iT = 0; iT < mesh-> n_cells(); iT++) {
556  Eigen::VectorXd XTF = nc.nc_restr(Xh, iT);
557  // Coefficient-wise power p
558  Eigen::VectorXd XTF_powerp = (XTF.array().abs()).pow(p);
559  // Sum local contributions; this assumes that MassT is diagonal
560  value += (MassT[iT] * XTF_powerp.matrix() ).sum();
561  }
562 
563  return std::pow(value, 1.0/p);
564 }
565 
566 double LEPNC_StefanPME_Transient::EnergyNorm(const Eigen::VectorXd Xh) const {
567  const Mesh* mesh = nc.get_mesh();
568  double value = 0.0;
569 
570  for (size_t iT = 0; iT < mesh-> n_cells(); iT++) {
571  Eigen::VectorXd XTF = nc.nc_restr(Xh, iT);
572  value += XTF.transpose() * DiffT[iT] * XTF;
573  }
574 
575  return sqrt(value);
576 }
577 
578 
579 
581 } // end of namespace HArDCore2D
582 
583 #endif //_LEPNC_STEFANPME_TRANSIENT_HPP
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 size_t n_dir_edges() const
Returns the number of Dirichlet edges.
Definition: BoundaryConditions.hpp:70
Definition: lepnccore.hpp:33
LEPNC scheme for diffusion equation .
Definition: LEPNC_StefanPME_transient.hpp:60
Definition: Mesh2D.hpp:26
std::function< double(double, std::string)> nonlinearity_function_type
type for nonlinear function
Definition: TestCaseNonLinearity.hpp:48
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
end
Definition: compute_eigs.m:12
Eigen::Vector2d VectorRd
Definition: basis.hpp:55
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
Eigen::VectorXd apply_nonlinearity(const Eigen::VectorXd &Y, const std::string type) const
Compute non-linearity on vector (depends if weight=0, weight=1 or weight\in (0,1) )
Definition: LEPNC_StefanPME_transient.hpp:366
Eigen::MatrixXd get_MassT(size_t iT) const
Mass matrix in cell iT.
Definition: LEPNC_StefanPME_transient.hpp:124
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
LEPNC_StefanPME_Transient(LEPNCCore &nc, tensor_function_type kappa, size_t deg_kappa, source_function_type source, BoundaryConditions BC, solution_function_type exact_solution, grad_function_type grad_exact_solution, TestCaseNonLinearity::nonlinearity_function_type zeta, double weight, std::string solver_type, std::ostream &output=std::cout)
Constructor of the class.
Definition: LEPNC_StefanPME_transient.hpp:166
Eigen::VectorXd iterate(const double tps, const double dt, const Eigen::VectorXd Xn)
Execute one time iteration.
Definition: LEPNC_StefanPME_transient.hpp:198
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
double L2_MassLumped(const Eigen::VectorXd Xh) const
Mass-lumped L2 norm of a function given by a vector.
Definition: LEPNC_StefanPME_transient.hpp:538
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
size_t get_nb_newton() const
number of Newton iterations
Definition: LEPNC_StefanPME_transient.hpp:120
std::function< VectorRd(const double &, const VectorRd &, const Cell *)> grad_function_type
type for gradient
Definition: LEPNC_StefanPME_transient.hpp:66
double EnergyNorm(const Eigen::VectorXd Xh) const
Discrete energy norm (associated to the diffusion operator)
Definition: LEPNC_StefanPME_transient.hpp:566
std::function< Eigen::Matrix2d(const double, const double, const Cell *)> tensor_function_type
type for diffusion tensor (does not depend on time)
Definition: LEPNC_StefanPME_transient.hpp:67
double get_solving_time() const
cpu time to solve the scheme
Definition: LEPNC_StefanPME_transient.hpp:108
double get_itime(size_t idx) const
various intermediate assembly times
Definition: LEPNC_StefanPME_transient.hpp:112
double Lp_MassLumped(const Eigen::VectorXd Xh, double p) const
Mass-lumped Lp norm of a function given by a vector.
Definition: LEPNC_StefanPME_transient.hpp:551
double get_solving_error() const
residual after solving the scheme
Definition: LEPNC_StefanPME_transient.hpp:116
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
std::function< double(const double &, const VectorRd &, const Cell *)> source_function_type
type for source
Definition: LEPNC_StefanPME_transient.hpp:65
double get_assembly_time() const
cpu time to assemble the scheme
Definition: LEPNC_StefanPME_transient.hpp:104
std::function< double(const double &, const VectorRd &)> solution_function_type
type for solution
Definition: LEPNC_StefanPME_transient.hpp:64
double measure() const
Return the Lebesgue measure of the Polytope.
Definition: Polytope2D.hpp:76
Polytope< DIMENSION > Cell
Definition: Polytope2D.hpp:151
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
std::size_t n_cells() const
number of cells in the mesh.
Definition: Mesh2D.hpp:63
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
A
Definition: mmread.m:102
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