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