HArD::Core2D
Hybrid Arbitrary Degree::Core 2D - Library to implement 2D schemes with edge and cell polynomials as unknowns
Loading...
Searching...
No Matches
HMM_StochTrans_StefanPME.hpp
Go to the documentation of this file.
1// Implementation of the HMM in 2D for the stochastic Stefan and PME models through the Doss-Sussman transform
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_STOCHTRANS_STEFANPME_HPP
14#define _HMM_STOCHTRANS_STEFANPME_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 <boost/math/constants/constants.hpp>
26#include <random>
27
28#include "mesh.hpp"
29#include "hybridcore.hpp"
30#include "quad2d.hpp"
34
35#ifdef WITH_MKL
36#include <Eigen/PardisoSupport>
37#include <mkl.h>
38#endif
39
45namespace HArDCore2D {
46
52// ----------------------------------------------------------------------------
53// Types and functions to define Wiener process
54// ----------------------------------------------------------------------------
55// Generic types
56template <typename T>
57using SpatialFunctionType = std::function<T(const VectorRd&)>;
58
59template <typename T>
60using PiecewiseSpatialFunctionType = std::function<T(const VectorRd&, const Cell*)>;
61
62template <typename T>
63using TemporalSpatialFunctionType = std::function<T(const double&, const VectorRd&)>;
64
65template <typename T>
66using PiecewiseTemporalSpatialFunctionType = std::function<T(const double&, const VectorRd&, const Cell*)>;
67
68static SpatialFunctionType<double> zero_scalar_function = [](const VectorRd & x)->double { return 0.;};
69
70template<typename T>
71using EigFunctionType = std::function<T(const size_t&, const size_t&, const VectorRd&)>;
72
73template<typename T>
74using DoubleVector = std::vector<std::vector<T>>;
75
76// Definition of eigenfunctions (depending on 2 indices)
77static const double PI = boost::math::constants::pi<double>();
78
79static std::function<double(const size_t &, const size_t &)> mui = [](const size_t &k, const size_t &l)->double
80 {
81 return 1./(std::pow(k, 2) + std::pow(l, 2));
82 };
83
84static EigFunctionType<double> mui_ei = [](const size_t &k, const size_t &l, const VectorRd & p)->double
85 {
86 return mui(k,l) * sin(k * PI * p.x()) * sin(l * PI * p.y());
87 };
88
89static EigFunctionType<VectorRd> grad_mui_ei = [](const size_t &k, const size_t &l, const VectorRd & p)->VectorRd
90 {
91 return mui(k,l) * VectorRd(
92 k * PI * cos(k * PI * p.x()) * sin(l * PI * p.y()),
93 l * PI * sin(k * PI * p.x()) * cos(l * PI * p.y())
94 );
95 };
96
97static EigFunctionType<double> Delta_mui_ei = [](const size_t &k, const size_t &l, const VectorRd & p)->double
98 {
99 return - mui(k,l) * (std::pow(k, 2) + std::pow(l, 2)) * std::pow(PI,2) * sin(k * PI * p.x()) * sin(l * PI * p.y());
100 };
101
102
103// Compute standard deviation of a vector
104static inline double StDev(const Eigen::VectorXd & v){
105 return sqrt( (v.array()*v.array()).mean() - std::pow(v.mean(), 2) );
106};
107
108// ----------------------------------------------------------------------------
109// Class definition
110// ----------------------------------------------------------------------------
111/* The StochStefanPME class provides an implementation of the HMM method for the stochastic Stefan and PME problems with Doss-Sussmann transformation.
112*/
114
116
117public:
118
119 // Types for solution
125
126 // types for Wiener process
130
133 HybridCore &hmm,
137 solution_function_type exact_solution,
138 grad_function_type grad_exact_solution,
139 solution_function_type time_der_exact_solution,
140 lapl_function_type minus_Lapl_exact_solution,
141 double weight,
142 std::string solver_type,
143 std::ostream & output = std::cout
144 );
145
148 const bool & use_exact_source,
149 const double & t,
150 const WienerType & W,
151 const GradWienerType & grad_W,
152 const WienerType & Delta_exp_W,
153 const ReactionType & mu_squared
154 );
155
157 std::pair<UVector, size_t> iterate(
158 const double tps,
159 const double dt,
160 const UVector& Xn,
161 const UVector& Ih_W,
162 const WienerType & W,
163 const ReactionType & mu_squared,
164 const source_function_type & source
165 );
166
168 void SetWienerProcess(
169 const int & caseW,
170 const double & t,
171 WienerType & W,
172 UVector & I_W,
173 GradWienerType & grad_W,
174 WienerType & Delta_exp_W,
175 ReactionType & mu_squared,
176 bool verbose = false
177 );
178
181 const double & dt,
182 DoubleVector<double> & Wtime,
183 WienerType & W,
184 const DoubleVector<Eigen::VectorXd> & Ih_eigs,
185 UVector & I_W,
186 GradWienerType & grad_W,
187 WienerType & Delta_exp_W,
188 ReactionType & mu_squared
189 );
190
192 Eigen::VectorXd apply_nonlinearity_eW(const std::string type, const Eigen::VectorXd & Y, const Eigen::VectorXd & I_W) const;
193 UVector apply_nonlinearity_eW(const std::string type, const UVector& Y, const Eigen::VectorXd & I_W) const;
194
196 double L2_MassLumped(const UVector& Xh) const;
197
199 double Lp_MassLumped(const UVector& Xh, double p) const;
200
202 double EnergyNorm(const UVector& Xh) const;
203
205 double Integral(const UVector& Xh) const;
206
208 inline double get_assembly_time() const {
209 return double(_assembly_time) * pow(10, -9);
210 }
211
213 inline double get_solution_time() const {
214 return double(_solution_time) * pow(10, -9);
215 }
216
218 inline double get_itime(size_t idx) const {
219 return double(_itime[idx]) * pow(10, -9);
220 }
221
223 inline double get_solving_error() const {
224 return _solving_error;
225 }
226
228 inline Eigen::MatrixXd get_MassT(size_t iT) const {
229 return MassT[iT];
230 }
231
232private:
234 Eigen::MatrixXd diffusion_operator(const size_t iT) const;
235
237 Eigen::VectorXd load_operator(const double tps, const size_t iT, const source_function_type & source) const;
238
241 Eigen::VectorXd residual_scheme(const double dt, const UVector& Xh, const std::vector<Eigen::VectorXd> & RHS_nl, const UVector& Ih_W, const ReactionType & mu_squared) const;
242
243 const HybridCore& m_hmm;
244 const tensor_function_type kappa;
246 const BoundaryConditions m_BC;
247 const solution_function_type exact_solution;
248 const grad_function_type grad_exact_solution;
249 const solution_function_type time_der_exact_solution;
250 const lapl_function_type minus_Lapl_exact_solution;
251 const double _weight;
252 const std::string solver_type;
253 std::ostream & m_output;
254
255 // For random numbers
256 std::random_device m_rd{};
257 std::mt19937 m_gen{m_rd()};
258
259 // To store local diffusion, mass and source terms
260 std::vector<Eigen::MatrixXd> DiffT;
261 std::vector<Eigen::MatrixXd> MassT;
262
263 // Computation statistics
264 size_t _assembly_time;
265 size_t _solution_time;
266 double _solving_error;
267 std::vector<double> _itime = std::vector<double>(10, 0.);
268
269};
270
271// Constructor
272StochStefanPME::StochStefanPME(HybridCore &hmm, tensor_function_type kappa, TestCaseNonLinearity::nonlinearity_function_type zeta, BoundaryConditions BC, solution_function_type exact_solution, grad_function_type grad_exact_solution, solution_function_type time_der_exact_solution, lapl_function_type minus_Lapl_exact_solution, double weight, std::string solver_type, std::ostream & output)
273 : m_hmm(hmm),
274 kappa(kappa),
275 zeta(zeta),
276 m_BC(BC),
277 exact_solution(exact_solution),
278 grad_exact_solution(grad_exact_solution),
279 time_der_exact_solution(time_der_exact_solution),
280 minus_Lapl_exact_solution(minus_Lapl_exact_solution),
281 _weight(weight),
282 solver_type(solver_type),
283 m_output(output) {
284
285 //---- Compute diffusion and mass matrix (do not change during time stepping or Newton) ------//
286 const Mesh* mesh = m_hmm.get_mesh();
287 DiffT.resize(mesh->n_cells());
288 MassT.resize(mesh->n_cells());
289 for (size_t iT = 0; iT < mesh->n_cells(); iT++) {
290 size_t n_edgesT = mesh->cell(iT)->n_edges();
291 size_t n_local_dofs = 1 + n_edgesT;
292 double measT = mesh->cell(iT)->measure();
293 DiffT[iT] = diffusion_operator(iT);
294 MassT[iT] = Eigen::MatrixXd::Zero(n_local_dofs, n_local_dofs);
295 MassT[iT](0,0) = (1-_weight) * measT;
296 MassT[iT].bottomRightCorner(n_edgesT, n_edgesT) = _weight * (measT / n_edgesT) * Eigen::MatrixXd::Identity(n_edgesT, n_edgesT);
297 }
298}
299
300
301//****************************************
302// Set the source
303//****************************************
305 const bool & use_exact_source,
306 const double & t,
307 const WienerType & W,
308 const GradWienerType & grad_W,
309 const WienerType & Delta_exp_W,
310 const ReactionType & mu_squared
311 ) {
312
314
315 if (use_exact_source){
316 // Exact source given by exact solution (latter is only valid for diffusion=Id)
317 source_term = [&](const VectorRd & p, const Cell* cell)->double {
318 double exp_W = exp(W(p));
319 double exp_minus_W = 1./exp_W;
320 VectorRd grad_exp_W = exp(W(p)) * grad_W(p);
321
322 double val = time_der_exact_solution(t, p)
323 - exp_minus_W * zeta(exp_W * exact_solution(t, p), "hess") *
324 (
325 grad_exp_W * exact_solution(t, p) + exp_W * grad_exact_solution(t, p, cell)
326 ).squaredNorm()
327 - exp_minus_W * zeta(exp_W * exact_solution(t, p), "der") *
328 (
329 Delta_exp_W(p) * exact_solution(t, p) + 2 * grad_exp_W.dot(grad_exact_solution(t,p,cell))
330 - exp_W * minus_Lapl_exact_solution(t, p, cell)
331 )
332 + 0.5 * mu_squared(p) * exact_solution(t, p);
333
334 return val;
335 };
336
337 }else{
338 source_term = [&](const VectorRd & p, const Cell* cell)->double { return 0.; };
339 }
340
341 return source_term;
342};
343
344
345//****************************************
346// Perform one time iteration
347//****************************************
348
349std::pair<UVector, size_t> StochStefanPME::iterate(const double tps, const double dt, const UVector& Xn, const UVector& Ih_W, const WienerType & W, const ReactionType & mu_squared, const source_function_type & source) {
350
351 boost::timer::cpu_timer timer;
352 boost::timer::cpu_timer timerint;
353 const auto mesh = m_hmm.get_mesh();
354 size_t n_edges_dofs = mesh->n_edges();
355 size_t n_cell_dofs = mesh->n_cells();
356
357 //----- COMPUTE SOURCE: load + previous time -----//
358 std::vector<Eigen::VectorXd> RHS_nl;
359 for (size_t iT = 0; iT < mesh->n_cells(); iT++) {
360 RHS_nl.push_back(dt * load_operator(tps, iT, source) + MassT[iT]*Xn.restr(iT));
361 }
362
363 //----- PARAMETERS FOR NEWTON ------------//
364 constexpr double tol = 1e-10;
365 constexpr size_t maxiter = 400;
366 // relaxation parameter
367 double relax = 1;
368 size_t iter = 0;
369 UVector Xhprev = Xn;
370 UVector Xh = UVector(Eigen::VectorXd::Zero(n_cell_dofs + n_edges_dofs), *m_hmm.get_mesh(), 0, 0);
371
372 // Vector of Dirichlet BC, either on u (if weight>0) or zeta(e^W u) (if weight=0)
373 Eigen::VectorXd DirBC = Eigen::VectorXd::Zero(n_cell_dofs + n_edges_dofs);
374 for (size_t ibF = 0; ibF < mesh->n_b_edges(); ibF++){
375 Edge* edge = mesh->b_edge(ibF);
376 if (m_BC.type(*edge)=="dir"){
377 size_t iF = edge->global_index();
379 for (QuadratureNode quadrule : quadF){
380 if (_weight == 0){
381 DirBC(n_cell_dofs + iF) += quadrule.w * zeta(exp(W(quadrule.vector())) * exact_solution(tps, quadrule.vector()), "fct");
382 }else{
383 DirBC(n_cell_dofs + iF) += quadrule.w * exact_solution(tps, quadrule.vector());
384 }
385 }
386 // Take average
387 DirBC(n_cell_dofs + iF) /= mesh->b_edge(ibF)->measure();
388 }
389 }
390 // Adjust initial iteration of Newton to match these BCs
391 Xhprev.asVectorXd().tail(mesh->n_b_edges()) = DirBC.tail(mesh->n_b_edges());
392
393 // ---- NEWTON ITERATONS ------ //
394 Eigen::VectorXd RES = residual_scheme(dt, Xhprev, RHS_nl, Ih_W, mu_squared); // Scheme is F(X)=C, then RES = C - F(u_prev)
395 std::vector<double> residual(maxiter, 1.0);
396 residual[iter] = RES.norm();
397
398 while ( (iter==0) || ( (iter < maxiter) && (residual[iter] > tol) ) ){
399 iter++;
400
401 timerint.start();
402 // System matrix and initialisation of RHS (only on the edge dofs)
403 Eigen::SparseMatrix<double> GlobMat(n_edges_dofs, n_edges_dofs);
404 std::vector<Eigen::Triplet<double>> triplets_GlobMat;
405 Eigen::VectorXd GlobRHS = RES.tail(n_edges_dofs);
406
407 // Static condensation: matrix and source to recover cell unknowns
408 Eigen::SparseMatrix<double> ScMat(n_cell_dofs, n_edges_dofs);
409 std::vector<Eigen::Triplet<double>> triplets_ScMat;
410 Eigen::VectorXd ScRHS = Eigen::VectorXd::Zero(n_cell_dofs);
411
412 // Assemble local contributions
413 for (size_t iT = 0; iT < mesh->n_cells(); iT++) {
414 Cell* T = mesh->cell(iT);
415 size_t n_edgesT = T->n_edges();
416
417 // exp(W) and exp(-W) in the cell
418 Eigen::VectorXd WT = Ih_W.restr(iT);
419 Eigen::MatrixXd exp_minus_WT = exp(-WT.array()).matrix().asDiagonal();
420
421 // Local static condensation of element unknowns
422 Eigen::VectorXd XTprev = Xhprev.restr(iT);
423 Eigen::VectorXd Zetaprime_expWT_XTprev = apply_nonlinearity_eW("der", XTprev, WT);
424 Eigen::MatrixXd MatZetaprime_expWT_XTprev = Zetaprime_expWT_XTprev.asDiagonal();
425
426 // Reaction term
427 Eigen::MatrixXd ReacT = MassT[iT];
428 ReacT(0,0) *= 0.5 * mu_squared(T->center_mass());
429 for (size_t i=0; i<n_edgesT; i++){
430 ReacT(i+1,i+1) *= 0.5 * mu_squared(T->edge(i)->center_mass());
431 }
432
433 Eigen::MatrixXd MatT = dt * exp_minus_WT * DiffT[iT] * MatZetaprime_expWT_XTprev + MassT[iT] + dt * ReacT;
434 Eigen::MatrixXd ATT = MatT.topLeftCorner(1, 1);
435 Eigen::MatrixXd ATF = MatT.topRightCorner(1, n_edgesT);
436 Eigen::MatrixXd AFT = MatT.bottomLeftCorner(n_edgesT, 1);
437 Eigen::MatrixXd AFF = MatT.bottomRightCorner(n_edgesT, n_edgesT);
438
439 Eigen::PartialPivLU<Eigen::MatrixXd> invATT;
440 invATT.compute(ATT);
441
442 Eigen::MatrixXd invATT_ATF = invATT.solve(ATF);
443 Eigen::VectorXd RES_cell = RES.segment(iT, 1);
444 Eigen::VectorXd invATT_RES_cell = invATT.solve(RES_cell);
445
446 // Local matrix and right-hand side on the face unknowns
447 Eigen::MatrixXd MatF = Eigen::MatrixXd::Zero(n_edgesT, n_edgesT);
448 Eigen::VectorXd bF = Eigen::VectorXd::Zero(n_edgesT);
449 MatF = AFF - AFT * invATT_ATF;
450 bF = - AFT * invATT_RES_cell; // only additional RHS coming from static condensation, beyond RES
451
452 // Assemble static condensation operator
453 ScRHS.segment(iT, 1) = invATT_RES_cell;
454
455 for (size_t i = 0; i < 1; i++){
456 size_t iGlobal = iT + i;
457 for (size_t j = 0; j < n_edgesT; j++){
458 size_t jGlobal = T->edge(j)->global_index();
459 triplets_ScMat.emplace_back(iGlobal, jGlobal, invATT_ATF(i, j));
460 }
461 }
462
463 // GLOBAL MATRIX and RHS on the edge unknowns, using the matrices MatF obtained after static condensation
464 for (size_t i = 0; i < n_edgesT; i++){
465 size_t iGlobal = T->edge(i)->global_index();
466 for (size_t j = 0; j < n_edgesT; j++){
467 size_t jGlobal = T->edge(j)->global_index();
468 triplets_GlobMat.emplace_back(iGlobal, jGlobal, MatF(i, j));
469 }
470 GlobRHS(iGlobal) += bF(i);
471 }
472 }
473
474
475 // Assemble the global linear system (without BC), and matrix to recover statically-condensed cell dofs
476 GlobMat.setFromTriplets(std::begin(triplets_GlobMat), std::end(triplets_GlobMat));
477 ScMat.setFromTriplets(std::begin(triplets_ScMat), std::end(triplets_ScMat));
478
479 // Dirichlet boundary conditions are trivial. Newton requires to solve
480 // F'(X^k) (X^{k+1} - X^k) = B - F(X^k)
481 // Both X^k and X^{k+1} have fixed Dirichlet values on boundary edges, so the system we solve
482 // is Dirichlet homogeneous, and we just select the non-dirichlet edges (first ones in the list)
483 size_t n_edge_unknowns = mesh->n_edges() - m_BC.n_dir_edges();
484 Eigen::SparseMatrix<double> A = GlobMat.topLeftCorner(n_edge_unknowns, n_edge_unknowns);
485//std::cout << "Number non-zero terms in matrix: " << A.nonZeros() << "\n";
486 Eigen::VectorXd B = GlobRHS.head(n_edge_unknowns);
487
488 timerint.stop();
489 _assembly_time += double(timerint.elapsed().wall);
490
491 timerint.start();
492 // Solve condensed system and recover cell unknowns. dX = X^{k+1}-X^k//
493 Eigen::VectorXd dX_edge_unknowns;
494 if(solver_type == "pardiso"){
495 #ifdef WITH_MKL
496 Eigen::PardisoLU<Eigen::SparseMatrix<double>> solver;
497 solver.compute(A);
498 if (solver.info() != Eigen::Success) {
499 std::cerr << "[main] ERROR: Could not factorize matrix" << std::endl;
500 }
501 dX_edge_unknowns = solver.solve(B);
502 if (solver.info() != Eigen::Success) {
503 std::cerr << "[main] ERROR: Could not solve linear system" << std::endl;
504 }
505 #endif
506 }else{
507 Eigen::BiCGSTAB<Eigen::SparseMatrix<double>, Eigen::IncompleteLUT<double> > solver;
508 solver.compute(A);
509 if (solver.info() != Eigen::Success) {
510 std::cerr << "[main] ERROR: Could not factorize matrix" << std::endl;
511 exit(1);
512 }
513 dX_edge_unknowns = solver.solve(B);
514 if (solver.info() != Eigen::Success) {
515 std::cerr << "[main] ERROR: Could not solve direct system" << std::endl;
516 exit(1);
517 }
518 }
519
520 // Recover all unknowns
521 Eigen::VectorXd dX = Eigen::VectorXd::Zero(n_cell_dofs + n_edges_dofs);
522 dX.segment(n_cell_dofs, n_edge_unknowns) = dX_edge_unknowns;
523 dX.head(n_cell_dofs) = ScRHS - ScMat * dX.tail(n_edges_dofs);
524
525 // Recover the fixed boundary values and cell unknowns (from static condensation and Newton).
526 // We start by putting the Dirichlet BC at the end.
527 Xh.asVectorXd() = DirBC;
528 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);
529
530 // Compute new residual. We might not accept new Xh but start over reducing relax. Otherwise, accept Xh and increase relax
531 // to a max of 1
532 Eigen::VectorXd RES_temp = residual_scheme(dt, Xh, RHS_nl, Ih_W, mu_squared);
533 double residual_temp = RES_temp.norm();
534
535 if (iter > 1 && residual_temp > 10*residual[iter-1]){
536 // We don't update the residual and Xh, but we will do another iteration with relax reduced
537 relax = relax/2.0;
538 iter--;
539 } else {
540 // Increase relax
541 relax = std::min(1.0, 1.2*relax);
542 // Store Xh in Xhprev, compute new residual
543 Xhprev.asVectorXd() = Xh.asVectorXd();
544 RES = RES_temp;
545 residual[iter] = residual_temp;
546 }
547 timerint.stop();
548 _solution_time += double(timerint.elapsed().wall);
549
550 // For a verbose output of Newton iterations:
551// m_output << " ...Iteration: " << iter << "; residual = " << residual[iter] << "; relax = "<< relax << std::endl;
552
553 } //-------- END NEWTON ITERATIONS -----------//
554
555// m_output << " (Newton it.: " << iter << ")" << std::endl;
556// m_output << "(" << iter << "), ";
557
558 return std::make_pair(Xh, iter);
559}
560
561
562//*****************************
563// Set the Wiener process
564//*****************************
565
566void StochStefanPME::SetWienerProcess(const int & caseW, const double & t, WienerType & W, UVector & I_W, GradWienerType & grad_W, WienerType & Delta_exp_W, ReactionType & mu_squared, bool verbose) {
567
568 switch(caseW){
569 case 0:
570 if(verbose) { std::cout << "[main] Wiener process = zero" << std::endl; }
571 W = [&](const VectorRd & p)->double { return 0.; };
572 grad_W = [&](const VectorRd & p)->VectorRd { return VectorRd::Zero(); };
573 Delta_exp_W = [&](const VectorRd & p)->double { return 0.; };
574 mu_squared = [](const VectorRd & p)->double { return 0.; };
575 break;
576
577 case 1:
578 if(verbose) { std::cout << "[main] Wiener process = x + 2y" << std::endl; }
579 W = [&](const VectorRd & p)->double { return p.x() + 2*p.y(); };
580 grad_W = [&](const VectorRd & p)->VectorRd { return VectorRd(1., 2.); };
581 Delta_exp_W = [&](const VectorRd & p)->double { return 5*exp(W(p)); };
582 mu_squared = [](const VectorRd & p)->double { return 0.; };
583 break;
584
585 case 2:
586 if(verbose) { std::cout << "[main] Wiener process = sin(pi x)sin(pi y)" << std::endl; }
587 W = [&](const VectorRd & p)->double { return mui_ei(1,1,p); };
588 grad_W = [&](const VectorRd & p)->VectorRd {
589 return grad_mui_ei(1,1,p);
590 };
591 Delta_exp_W = [&](const VectorRd & p)->double {
592 return exp(W(p)) * (Delta_mui_ei(1,1,p) + grad_W(p).squaredNorm());
593 };
594 mu_squared = [](const VectorRd & p)->double { return std::pow(mui_ei(1,1,p), 2); };
595 break;
596
597 case 3:
598 if(verbose) { std::cout << "[main] Wiener process = cos(t) * sin(pi x)sin(pi y)" << std::endl; }
599 W = [&](const VectorRd & p)->double { return cos(t) * mui_ei(1,1,p); };
600 grad_W = [&](const VectorRd & p)->VectorRd {
601 return cos(t) * grad_mui_ei(1,1,p);
602 };
603 Delta_exp_W = [&](const VectorRd & p)->double {
604 return exp(W(p)) * ( cos(t) * Delta_mui_ei(1,1,p) + grad_W(p).squaredNorm() );
605 };
606 mu_squared = [](const VectorRd & p)->double { return std::pow(mui_ei(1,1,p), 2); };
607 break;
608
609 default:
610 std::cout << "[main] Wiener process unknown" << std::endl;
611 exit(1);
612 }
613
614 I_W.asVectorXd() = m_hmm.interpolate(W, 0, 0, 3).asVectorXd();
615};
616
617
619void StochStefanPME::SimulateWienerProcess(const double & dt, DoubleVector<double> & Wtime, WienerType & W, const DoubleVector<Eigen::VectorXd> & Ih_eigs, UVector & I_W, GradWienerType & grad_W, WienerType & Delta_exp_W, ReactionType & mu_squared) {
620
621 size_t nb_eig = Wtime.size();
622
623 // Simulate increment of Wtime
624 std::normal_distribution<double> Nd{0, sqrt(dt)};
625 for (size_t k=0; k<nb_eig; ++k){
626 for (size_t l=0; l<nb_eig; ++l){
627 Wtime[k][l] += Nd(m_gen);
628 }
629 }
630
631 // Compute Wiener process
632 W = [&, nb_eig](const VectorRd & p)->double {
633 double val = 0;
634 for (size_t k=0; k<nb_eig; ++k){
635 for (size_t l=0; l<nb_eig; ++l){
636 val += Wtime[k][l] * mui_ei(k+1, l+1, p);
637 }
638 }
639 return val;
640 };
641 grad_W = [&, nb_eig](const VectorRd & p)->VectorRd {
642 VectorRd val = VectorRd::Zero();
643 for (size_t k=0; k<nb_eig; ++k){
644 for (size_t l=0; l<nb_eig; ++l){
645 val += Wtime[k][l] * grad_mui_ei(k+1, l+1, p);
646 }
647 }
648 return val;
649 };
650 Delta_exp_W = [&, nb_eig](const VectorRd & p)->double {
651 double Delta_Wp = 0.;
652 VectorRd grad_Wp = VectorRd::Zero();
653 for (size_t k=0; k<nb_eig; ++k){
654 for (size_t l=0; l<nb_eig; ++l){
655 Delta_Wp += Wtime[k][l] * Delta_mui_ei(k+1, l+1, p);
656 grad_Wp += Wtime[k][l] * grad_mui_ei(k+1, l+1, p);
657 }
658 }
659 return exp(W(p)) * (Delta_Wp + grad_Wp.squaredNorm());
660 };
661 mu_squared = [&, nb_eig](const VectorRd & p)->double {
662 double val = 0.;
663 for (size_t k=0; k<nb_eig; ++k){
664 for (size_t l=0; l<nb_eig; ++l){
665 val += std::pow(mui_ei(k+1, l+1, p), 2);
666 }
667 }
668 return val;
669 };
670
671 // Compute interpolate
672 I_W.asVectorXd() = Eigen::VectorXd::Zero(I_W.asVectorXd().size());
673 for (size_t k=0; k<nb_eig; ++k){
674 for (size_t l=0; l<nb_eig; ++l){
675 I_W.asVectorXd() += Wtime[k][l] * Ih_eigs[k][l];
676 }
677 }
678
679};
680
681
682//****************************************
683// Apply nonlinearity zeta to a vector
684//****************************************
685
686Eigen::VectorXd StochStefanPME::apply_nonlinearity_eW(const std::string type, const Eigen::VectorXd & Y, const Eigen::VectorXd & W) const {
687 // Type="fct" or "der" as per the nonlinear function zeta
688 // The function applied to each coefficient of Y depends on its nature. If it's a coefficient appearing in
689 // the mass-lumping (which depends on _weight), the unknown represents u and we apply the full nonlinear function zeta after multiplying by e^W.
690 // Otherwise, the unknown is zeta(e^W u) itself and the function we apply is the identity s->s (with nlin/der: s-> 1)
691 // The derivative also takes into account the potential factor e^W that comes out when the unknown is u itself
692
693 Eigen::VectorXd ZetaY;
694 // Initialisation depends on "type". We initialise as if the nonlinearity was the identity, we'll change the
695 // appropriate coefficients afterwards
696 if (type == "fct"){
697 ZetaY = Y;
698 }else if (type == "der"){
699 ZetaY = Eigen::VectorXd::Ones(Y.size());
700 }else{
701 m_output << "type unknown in apply_nonlinearity: " << type << "\n";
702 exit(EXIT_FAILURE);
703 }
704
705 const Mesh* mesh = m_hmm.get_mesh();
706 // We check if Y is a local vector or a global one, this determines the number of cell and edge unknowns
707 size_t n_cell_dofs = 0;
708 if (size_t(Y.size()) == mesh->n_cells() + mesh->n_edges()){
709 n_cell_dofs = mesh->n_cells();
710 }else{
711 n_cell_dofs = 1;
712 }
713 size_t n_edges_dofs = Y.size() - n_cell_dofs;
714
715 // Determine range of application of function (cell, all, or edge unknowns)
716 size_t start = 0;
717 size_t end = n_cell_dofs + n_edges_dofs;
718 if (_weight == 0){
719 start = 0;
720 end = n_cell_dofs;
721 }else if (_weight == 1){
722 start = n_cell_dofs;
723 end = n_cell_dofs + n_edges_dofs;
724 }
725
726 // Apply non-linearity and correct for derivative
727 for (size_t i = start; i < end; i++){
728 ZetaY(i) = zeta(exp(W(i)) * Y(i), type);
729 if (type == "der"){
730 ZetaY(i) *= exp(W(i));
731 }
732 }
733
734 return ZetaY;
735}
736
737// Overloaded version: apply to the values of UVector, don't change the other parameters
738UVector StochStefanPME::apply_nonlinearity_eW(const std::string type, const UVector & Y, const Eigen::VectorXd & W) const {
739
740 UVector ZetaY = Y;
741 ZetaY.asVectorXd() = apply_nonlinearity_eW(type, Y.asVectorXd(), W);
742 return ZetaY;
743}
744
745
746//*****************************************
747// Local diffusion matrix and load term
748//*****************************************
749
750Eigen::MatrixXd StochStefanPME::diffusion_operator(const size_t iT) const {
751
752 const auto mesh = m_hmm.get_mesh();
753 size_t dim = mesh->dim();
754 Cell* T = mesh->cell(iT);
755 double mT = T->measure();
756 const size_t n_edgesT = T->n_edges();
757
758 size_t local_dofs = 1 + n_edgesT;
759 Eigen::MatrixXd StiffT = Eigen::MatrixXd::Zero(local_dofs, local_dofs);
760
761 // Diffusion in the cell.
762 Eigen::Vector2d xT = T->center_mass();
763 Eigen::Matrix2d kappaT = kappa(xT, T);
764
765 // Matrix of \nabla_K
766 Eigen::MatrixXd nablaK = Eigen::MatrixXd::Zero(dim, local_dofs);
767 for (size_t iE=0; iE < n_edgesT; iE++){
768 Edge* E = T->edge(iE);
769 double mE = E->measure();
770 nablaK.block(0, iE+1, dim, 1) = mE * T->edge_normal(iE);
771 }
772 nablaK /= mT;
773
774 // Contribution per diamond
775 for (size_t iE=0; iE < n_edgesT; iE++){
776 Edge* E = T->edge(iE);
777 Eigen::Vector2d xE = E->center_mass();
778 Eigen::Vector2d nTE = T->edge_normal(iE);
779 double mE = E->measure();
780 // Distance center to edge and measure of diamond
781 double dTE = (xE-xT).dot(nTE);
782 double mTE = mE * dTE / dim;
783
784 // stabilisation (without scaling or normal: u_E-u_K-nablaK u . (xE-xK))
785 Eigen::MatrixXd stabE = Eigen::MatrixXd::Zero(1, local_dofs);
786 stabE(0) = -1.0;
787 stabE(1+iE) = 1.0;
788 stabE -= (xE-xT).transpose() * nablaK;
789
790 // Complete gradient with stabilisation
791 Eigen::MatrixXd GRAD = nablaK + (pow(dim, 0.5)/dTE) * nTE * stabE;
792
793 // Contribution to stiffness
794 StiffT += mTE * GRAD.transpose() * kappaT * GRAD;
795 }
796
797 return StiffT;
798}
799
800
801Eigen::VectorXd StochStefanPME::load_operator(const double tps, const size_t iT, const source_function_type & source) const {
802
803 const auto mesh = m_hmm.get_mesh();
804 Cell* T = mesh->cell(iT);
805 size_t n_edgesT = T->n_edges();
806 size_t local_dofs = 1 + n_edgesT;
807
808 // Beware, MassT[iT] must have been created
809 if (MassT.size() < iT || size_t(MassT[iT].rows()) != local_dofs){
810 m_output << "Called load_operator without creating MassT[iT]\n";
811 exit(EXIT_FAILURE);
812 }
813 Eigen::VectorXd fvec = Eigen::VectorXd::Zero(1 + n_edgesT);
814 for (size_t i = 0; i < local_dofs; i++){
815 VectorRd node;
816 if (i == 0){
817 node = T->center_mass();
818 }else{
819 node = T->edge(i-1)->center_mass();
820 }
821 fvec(i) = source(node, T);
822 }
823 // Contribution of source to loading term
824 Eigen::VectorXd load = MassT[iT] * fvec;
825
826 // Adding Neumann boundary conditions
827 // The boundary conditions coming from Neumann edges are computed based on the piecewise constant
828 // discrete trace: if g is the value of the outer flux,
829 // \int g v -> \int g Tv
830 // where (Tv)_e = average of v on e, for e Neumann edge.
831 // Only edge-based basis functions therefore have a contribution, which will simply be \int_e g.
832 if (T->is_boundary()){
833 for (size_t ilF = 0; ilF < T->n_edges(); ilF++) {
834 Edge* edge = T->edge(ilF);
835 if (m_BC.type(*edge)=="neu"){
836 if (edge->is_boundary()){
837 const auto& nTF = T->edge_normal(ilF);
839 std::function<double(VectorRd)> Kgrad_n = [&](VectorRd p){
840 return zeta(exact_solution(tps, p), "der") * nTF.dot(kappa(p,T) * grad_exact_solution(tps, p, T));
841 };
842 for (QuadratureNode qr : quadF){
843 load(DimPoly<Cell>(1) + ilF) += qr.w * Kgrad_n(qr.vector());
844 }
845 }
846 }
847 }
848 }
849
850 return load;
851}
852
853//***********************************************************************
854// Residual non-linear scheme: if scheme is F(X)=C, residual is C-F(X)
855//***********************************************************************
856
857Eigen::VectorXd StochStefanPME::residual_scheme(const double dt, const UVector& Xh, const std::vector<Eigen::VectorXd> & RHS_nl, const UVector& Ih_W, const ReactionType & mu_squared) const{
858
859 const Mesh* mesh = m_hmm.get_mesh();
860 Eigen::VectorXd RES = Eigen::VectorXd::Zero(mesh->n_cells() + mesh->n_edges());
861
862 // Compute the residual: C - (dt * e^(-W) * DIFF * Zeta(e^W Xh) + MASS * Xh), where DIFF is the diffusion matrix, MASS is the mass matrix and C is the source term
863 size_t n_cell_dofs = mesh->n_cells();
864 for (size_t iT = 0; iT < mesh->n_cells(); iT++){
865 Cell* T = mesh->cell(iT);
866 size_t n_edgesT = T->n_edges();
867
868 // exp(W) and exp(-W) in the cell
869 Eigen::VectorXd WT = Ih_W.restr(iT);
870 Eigen::MatrixXd exp_minus_WT = exp(-WT.array()).matrix().asDiagonal();
871
872 // Local unknown in the cell, and zeta of these unknowns
873 Eigen::VectorXd XT = Xh.restr(iT);
874 Eigen::VectorXd Zeta_expWT_XT = apply_nonlinearity_eW("fct", XT, WT);
875
876 // Reaction term
877 Eigen::MatrixXd ReacT = MassT[iT];
878 ReacT(0,0) *= 0.5 * mu_squared(T->center_mass());
879 for (size_t i=0; i<n_edgesT; i++){
880 ReacT(i+1,i+1) *= 0.5 * mu_squared(T->edge(i)->center_mass());
881 }
882
883 Eigen::VectorXd REST = RHS_nl[iT] - (dt * exp_minus_WT * DiffT[iT] * Zeta_expWT_XT + (MassT[iT] + dt * ReacT) * XT);
884 RES(iT) += REST(0);
885 for (size_t ilE = 0; ilE < n_edgesT; ilE++){
886 size_t iE = T->edge(ilE)->global_index();
887 RES(n_cell_dofs + iE) += REST(1+ilE);
888 }
889 }
890
891 // No residual on Dirichlet edges (do not correspond to equations of the system)
892 RES.tail(m_BC.n_dir_edges()) = Eigen::VectorXd::Zero(m_BC.n_dir_edges());
893
894 return RES;
895}
896
897//*******************************************************
898// Norms: L2 mass lumped, Energy (pure diffusion)
899//*******************************************************
900
901double StochStefanPME::L2_MassLumped(const UVector& Xh) const {
902 const Mesh* mesh = m_hmm.get_mesh();
903 double value = 0.0;
904
905 for (size_t iT = 0; iT < mesh-> n_cells(); iT++) {
906 Eigen::VectorXd XTF = Xh.restr(iT);
907 value += XTF.transpose() * MassT[iT] * XTF;
908 }
909
910 return sqrt(value);
911}
912
913
914double StochStefanPME::Lp_MassLumped(const UVector& Xh, const double p) const {
915 const Mesh* mesh = m_hmm.get_mesh();
916 double value = 0.0;
917
918 for (size_t iT = 0; iT < mesh-> n_cells(); iT++) {
919 Eigen::VectorXd XTF = Xh.restr(iT);
920 // Coefficient-wise power p
921 Eigen::VectorXd XTF_powerp = (XTF.array().abs()).pow(p);
922 // Sum local contributions; this assumes that MassT is diagonal
923 value += (MassT[iT] * XTF_powerp.matrix() ).sum();
924 }
925
926 return std::pow(value, 1.0/p);
927}
928
929double StochStefanPME::EnergyNorm(const UVector& Xh) const {
930 const Mesh* mesh = m_hmm.get_mesh();
931 double value = 0.0;
932
933 for (size_t iT = 0; iT < mesh-> n_cells(); iT++) {
934 Eigen::VectorXd XTF = Xh.restr(iT);
935 value += XTF.transpose() * DiffT[iT] * XTF;
936 }
937
938 return sqrt(value);
939}
940
941double StochStefanPME::Integral(const UVector& Xh) const {
942 const Mesh* mesh = m_hmm.get_mesh();
943 double value = 0.0;
944
945 for (size_t iT = 0; iT < mesh-> n_cells(); iT++) {
946 value += (MassT[iT] * Xh.restr(iT)).sum();
947 }
948
949 return value;
950}
951
952
953
954
956} // end of namespace HArDCore2D
957
958#endif //_HMM_STOCHTRANS_STEFANPME_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 hybridcore.hpp:179
The vector Xh manipulated in the resolution has mixed components, corresponding either to the unknown...
Definition HMM_StochTrans_StefanPME.hpp:115
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
end
Definition compute_eigs.m:12
Eigen::Vector2d VectorRd
Definition basis.hpp:55
static const double PI
Definition ddr-klplate.hpp:187
Eigen::MatrixXd get_MassT(size_t iT) const
Mass matrix in cell iT.
Definition HMM_StochTrans_StefanPME.hpp:228
std::vector< std::vector< T > > DoubleVector
Definition HMM_StochTrans_StefanPME.hpp:74
SpatialFunctionType< double > ReactionType
Type for reaction term mu^2.
Definition HMM_StochTrans_StefanPME.hpp:129
std::function< T(const size_t &, const size_t &, const VectorRd &)> EigFunctionType
Type for "eigenfunctions" e_i, depending on two indices k,l.
Definition HMM_StochTrans_StefanPME.hpp:71
double get_itime(size_t idx) const
various intermediate assembly times
Definition HMM_StochTrans_StefanPME.hpp:218
double Integral(const UVector &Xh) const
Integral of a function given by a vector.
Definition HMM_StochTrans_StefanPME.hpp:941
std::function< T(const double &, const VectorRd &)> TemporalSpatialFunctionType
Generic type for function that depends on space and time.
Definition HMM_StochTrans_StefanPME.hpp:63
double EnergyNorm(const UVector &Xh) const
Discrete energy norm (associated to the diffusion operator)
Definition HMM_StochTrans_StefanPME.hpp:929
double get_solving_error() const
residual after solving the scheme
Definition HMM_StochTrans_StefanPME.hpp:223
static std::function< double(const size_t &, const size_t &)> mui
Definition HMM_StochTrans_StefanPME.hpp:79
std::pair< UVector, size_t > iterate(const double tps, const double dt, const UVector &Xn, const UVector &Ih_W, const WienerType &W, const ReactionType &mu_squared, const source_function_type &source)
Execute one time iteration: return the calculated vector, and the number of Newton iterations.
Definition HMM_StochTrans_StefanPME.hpp:349
void SimulateWienerProcess(const double &dt, DoubleVector< double > &Wtime, WienerType &W, const DoubleVector< Eigen::VectorXd > &Ih_eigs, UVector &I_W, GradWienerType &grad_W, WienerType &Delta_exp_W, ReactionType &mu_squared)
Set the Wiener process, random case.
Definition HMM_StochTrans_StefanPME.hpp:619
PiecewiseTemporalSpatialFunctionType< double > lapl_function_type
type for laplacian
Definition HMM_StochTrans_StefanPME.hpp:123
std::function< T(const VectorRd &, const Cell *)> PiecewiseSpatialFunctionType
Generic type for function that depends on space, with formula changing from one cell to the next.
Definition HMM_StochTrans_StefanPME.hpp:60
StochStefanPME(HybridCore &hmm, tensor_function_type kappa, TestCaseNonLinearity::nonlinearity_function_type zeta, BoundaryConditions BC, solution_function_type exact_solution, grad_function_type grad_exact_solution, solution_function_type time_der_exact_solution, lapl_function_type minus_Lapl_exact_solution, double weight, std::string solver_type, std::ostream &output=std::cout)
Constructor of the class.
Definition HMM_StochTrans_StefanPME.hpp:272
std::function< T(const double &, const VectorRd &, const Cell *)> PiecewiseTemporalSpatialFunctionType
Generic type for function that depends on space and time, with formula changing from one cell to the ...
Definition HMM_StochTrans_StefanPME.hpp:66
Eigen::VectorXd apply_nonlinearity_eW(const std::string type, const Eigen::VectorXd &Y, const Eigen::VectorXd &I_W) const
Compute non-linearity and e^W on vector Y (depends if weight=0, weight=1 or weight\in (0,...
Definition HMM_StochTrans_StefanPME.hpp:686
double L2_MassLumped(const UVector &Xh) const
Mass-lumped L2 norm of a function given by a vector.
Definition HMM_StochTrans_StefanPME.hpp:901
double get_solution_time() const
cpu time to solve the linear systems
Definition HMM_StochTrans_StefanPME.hpp:213
static SpatialFunctionType< double > zero_scalar_function
Zero spatial function.
Definition HMM_StochTrans_StefanPME.hpp:68
std::function< T(const VectorRd &)> SpatialFunctionType
Generic type for function that only depends on spatial coordinate.
Definition HMM_StochTrans_StefanPME.hpp:57
PiecewiseSpatialFunctionType< double > source_function_type
type for source (at a given fixed time)
Definition HMM_StochTrans_StefanPME.hpp:121
static double StDev(const Eigen::VectorXd &v)
Definition HMM_StochTrans_StefanPME.hpp:104
PiecewiseSpatialFunctionType< Eigen::Matrix2d > tensor_function_type
type for diffusion tensor (does not depend on time)
Definition HMM_StochTrans_StefanPME.hpp:124
void SetWienerProcess(const int &caseW, const double &t, WienerType &W, UVector &I_W, GradWienerType &grad_W, WienerType &Delta_exp_W, ReactionType &mu_squared, bool verbose=false)
Set the Wiener process, deterministic case.
Definition HMM_StochTrans_StefanPME.hpp:566
static EigFunctionType< double > Delta_mui_ei
Definition HMM_StochTrans_StefanPME.hpp:97
TemporalSpatialFunctionType< double > solution_function_type
type for solution
Definition HMM_StochTrans_StefanPME.hpp:120
double get_assembly_time() const
cpu time to assemble the scheme
Definition HMM_StochTrans_StefanPME.hpp:208
SpatialFunctionType< VectorRd > GradWienerType
type for gradient of Wiener process
Definition HMM_StochTrans_StefanPME.hpp:128
static EigFunctionType< VectorRd > grad_mui_ei
Definition HMM_StochTrans_StefanPME.hpp:89
PiecewiseTemporalSpatialFunctionType< VectorRd > grad_function_type
type for gradient
Definition HMM_StochTrans_StefanPME.hpp:122
SpatialFunctionType< double > WienerType
type for the Wiener process at a fixed time (only variation in space)
Definition HMM_StochTrans_StefanPME.hpp:127
static EigFunctionType< double > mui_ei
Definition HMM_StochTrans_StefanPME.hpp:84
double Lp_MassLumped(const UVector &Xh, double p) const
Mass-lumped Lp norm of a function given by a vector.
Definition HMM_StochTrans_StefanPME.hpp:914
StochStefanPME::source_function_type compute_source(const bool &use_exact_source, const double &t, const WienerType &W, const GradWienerType &grad_W, const WienerType &Delta_exp_W, const ReactionType &mu_squared)
Compute the source to zero or the exact one based on the selected solution and Wiener process.
Definition HMM_StochTrans_StefanPME.hpp:304
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
UVector interpolate(const ContinuousFunction &f, const int deg_cell, const size_t deg_edge, size_t doe) const
Compute the interpolant in the discrete space of a continuous function.
Definition hybridcore.hpp:290
Eigen::VectorXd & asVectorXd() const
Return the values as an Eigen vector.
Definition hybridcore.hpp:92
const 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
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
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
static auto v
Definition ddrcore-test.hpp:32
Description of one node and one weight from a quadrature rule.
Definition quadraturerule.hpp:41