deal.II version GIT relicensing-6750-g1dc21bc838 2026-09-15 17:20:01+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
MLMC_random_darcy_flow.h
Go to the documentation of this file.
1
111 *   #pragma once
112 *   #include <deal.II/base/point.h>
113 *   #include <deal.II/base/function.h>
114 *  
115 *   #include <iostream>
116 *   #include <vector>
117 *  
118 *   namespace RandomField
119 *   {
120 *   using namespace dealii;
121 *   /* We assume here that our auto-covariance function is exponential, but not Gaussian.
122 *   This is both modeling choice and mathematically reasonable in this case.
123 *   We also assume a variance of 1 here. */
124 *   template <int dim>
125 *   class KLExpansion
126 *   {
127 *   public:
128 *   KLExpansion(const unsigned int n_terms, double L, double l, double mu);
129 *  
130 *   double compute_kl_expansion(const Point<dim>& p, std::vector<double> &samples);
131 *  
132 *   private:
133 *  
134 * @endcode
135 *
136 * computes our coefficients
137 *
138
139 *
140 * computes our eigenvalues based on the previously computed frequencies
141 *
142 * @code
143 *   void compute_lambda_i();
144 * @endcode
145 *
146 * computes norm values such that our eigenfunctions are normed to one.
147 *
148 * @code
149 *   void compute_alpha_i();
150 * @endcode
151 *
152 * compute the frequencies
153 *
154 * @code
155 *   void compute_omega_i();
156 *  
157 * @endcode
158 *
159 * functions we need for the computation of omega_i
160 *
161 * @code
162 *   double f_even(double sol);
163 *   double f_odd(double sol);
164 *   double grad_f_even(double sol);
165 *   double grad_f_odd(double sol);
166 *  
167 * @endcode
168 *
169 * newton since equation is nonlinear
170 *
171 * @code
172 *   void newton_even(unsigned int index);
173 *   void newton_odd(unsigned int index);
174 *  
175 * @endcode
176 *
177 * number of KL expansion terms
178 *
179 * @code
180 *   unsigned int n_terms_;
181 * @endcode
182 *
183 * domain length
184 *
185 * @code
186 *   double domain_length_;
187 * @endcode
188 *
189 * correlation length
190 *
191 * @code
192 *   double correlation_length_;
193 * @endcode
194 *
195 * important parameters for the construction of the KL-Expansion in x-direction
196 *
197 * @code
198 *   std::vector<double> omega_i;
199 *   std::vector<double> lambda_i;
200 *   std::vector<double> alpha_i;
201 *  
202 * @endcode
203 *
204 * constant mean of our field.
205 *
206 * @code
207 *   double mu_;
208 *   };
209 *   }
210 * @endcode
211
212
213<a name="ann-include/mlmc.h"></a>
214<h1>Annotated version of include/mlmc.h</h1>
215 *
216 *
217 *
218 *
219 * @code
220 *   /* -----------------------------------------------------------------------------
221 *   *
222 *   * SPDX-License-Identifier: LGPL-2.1-or-later
223 *   * Copyright (C) 2026 by Jonas Plank
224 *   *
225 *   * This file is part of the deal.II code gallery.
226 *   *
227 *   * -----------------------------------------------------------------------------
228 *   */
229 *   #pragma once
230 *   #include <vector>
231 *   #include <random>
232 *  
233 *   namespace MultilevelMonteCarlo
234 *   {
235 *   template <int dim>
236 *   class MLMC
237 *   {
238 *   public:
239 *   MLMC(unsigned int oneD_samples);
240 *  
241 * @endcode
242 *
243 * generates the required number of samples
244 *
245 * @code
246 *   std::vector<double> generate_samples();
247 *  
248 * @endcode
249 *
250 * store the samples for the computation of mean and variance
251 *
252 * @code
253 *   void add_sample(double rvalue);
254 *  
255 *   double compute_mean();
256 *  
257 *   double compute_variance();
258 * @endcode
259 *
260 * removes all samples after reached convergence on a level
261 *
262 * @code
263 *   void clear_samples();
264 *  
265 *   private:
266 *   std::mt19937 rng; // Mersenne Twister engine
267 *   std::normal_distribution<double> dist; // Normal distribution
268 *   unsigned int num_samples;
269 *  
270 * @endcode
271 *
272 * parameters for error computation
273 *
274 * @code
275 *   std::vector<double> results;
276 *   };
277 *   }
278 *  
279 * @endcode
280
281
282<a name="ann-include/random_darcy.h"></a>
283<h1>Annotated version of include/random_darcy.h</h1>
284 *
285 *
286 *
287 *
288 * @code
289 *   /* -----------------------------------------------------------------------------
290 *   *
291 *   * SPDX-License-Identifier: LGPL-2.1-or-later
292 *   * Copyright (C) 2026 by Jonas Plank
293 *   *
294 *   * This file is part of the deal.II code gallery.
295 *   *
296 *   * -----------------------------------------------------------------------------
297 *   */
298 *   #pragma once
299 *   #include <deal.II/base/quadrature_lib.h>
300 *   #include <deal.II/dofs/dof_handler.h>
301 *   #include <deal.II/dofs/dof_tools.h>
302 *   #include <deal.II/fe/fe_values.h>
303 *   #include <deal.II/grid/tria.h>
304 *   #include <deal.II/grid/grid_generator.h>
305 *   #include <deal.II/lac/dynamic_sparsity_pattern.h>
306 *   #include <deal.II/lac/full_matrix.h>
307 *   #include <deal.II/lac/sparse_matrix.h>
308 *   #include <deal.II/lac/vector.h>
309 *   #include <deal.II/numerics/data_out.h>
310 *   #include <deal.II/numerics/vector_tools.h>
311 *   #include <deal.II/fe/fe_q.h>
312 *   #include <deal.II/grid/grid_out.h>
313 *   #include <deal.II/lac/affine_constraints.h>
314 *   #include <deal.II/grid/grid_refinement.h>
315 *   #include <deal.II/numerics/error_estimator.h>
316 *   #include <deal.II/base/function.h>
317 *   #include <fstream>
318 *  
319 *   #include "random_permeability.h"
320 *  
321 *   namespace Discretization
322 *   {
323 *   using namespace dealii;
324 *  
325 *  
331 *   template <int dim>
332 *   class RandomDarcy
333 *   {
334 *   public:
335 *   RandomDarcy();
336 *  
337 * @endcode
338 *
339 * Allows switching between coarse and fine triangulations
340 * with virtually no code duplication.
341 *
342 * @code
343 *   void set_tria(bool fine);
344 *  
345 *   void generate_mesh(double domain_length);
346 *  
347 * @endcode
348 *
349 * Implementation follows the logic of deal.II tutorial @ref step_5 "step-5".
350 *
351 * @code
352 *   void setup_system();
353 *  
354 *   void assemble_system(RandomField::RandomPermeability<dim>& random_constant);
355 *  
356 *  
361 *   void solve();
362 *  
363 * @endcode
364 *
365 * If we are on the first level, we only want to refine the fine triangulation.
366 *
367 * @code
368 *   void refine_grid(bool firstRun);
369 *  
370 * @endcode
371 *
372 * On the first run, we label the output as "coarse" since both meshes
373 * are identical. After the first level, the meshes diverge, and we
374 * output the finer mesh.
375 *
376 * @code
377 *   void output_results(bool firstRun, unsigned int level);
378 *  
379 * @endcode
380 *
381 * This is our Quantity of Interest (QoI).
382 * It is defined as: @f$K_{eff} = - \int_{\Gamma_{right}} k \frac{\partial p}{\partial x_1} dx_2@f$
383 *
384 * @code
385 *   double compute_Keff(RandomField::RandomPermeability<dim>& permeability);
386 *  
387 *   private:
388 * @endcode
389 *
390 * define two triangulations
391 *
392 * @code
393 *   Triangulation<dim> coarse_tria;
394 *   Triangulation<dim> fine_tria;
395 *  
396 *   const FE_Q<dim> fe;
397 *   DoFHandler<dim> dof_handler;
398 *  
399 *   AffineConstraints<double> constraints;
400 *  
401 *   SparseMatrix<double> system_matrix;
402 *   SparsityPattern sparsity_pattern;
403 *  
404 *   Vector<double> solution;
405 *   Vector<double> system_rhs;
406 *   };
407 *   }
408 * @endcode
409
410
411<a name="ann-include/random_permeability.h"></a>
412<h1>Annotated version of include/random_permeability.h</h1>
413 *
414 *
415 *
416 *
417 * @code
418 *   /* -----------------------------------------------------------------------------
419 *   *
420 *   * SPDX-License-Identifier: LGPL-2.1-or-later
421 *   * Copyright (C) 2026 by Jonas Plank
422 *   *
423 *   * This file is part of the deal.II code gallery.
424 *   *
425 *   * -----------------------------------------------------------------------------
426 *   */
427 *   #pragma once
428 *   #include "KL_expansion.h"
429 *  
430 *   namespace RandomField
431 *   {
432 *   using namespace dealii;
433 *  
434 *   /*
435 *   This class is really our interface between the KL expansion
436 *   and the FEM code. */
437 *   template <int dim>
438 *   class RandomPermeability : public Function<dim>
439 *   {
440 *   public:
441 *   RandomPermeability(std::vector<double> &first_sample, unsigned int n_terms, double domain_length, double correlation_length, double mu);
442 *  
443 * @endcode
444 *
445 * overwrite the samples
446 *
447 * @code
448 *   void overwrite_samples(const std::vector<double> &next_sample);
449 *  
450 * @endcode
451 *
452 * our evaluator
453 *
454 * @code
455 *   double value(const Point<dim>& p);
456 *  
457 *   private:
458 *   KLExpansion<dim> kl_expansion;
459 *   std::vector<double> samples;
460 *   };
461 *   }
462 *  
463 * @endcode
464
465
466<a name="ann-main.cc"></a>
467<h1>Annotated version of main.cc</h1>
468 *
469 *
470 *
471 *
472 * @code
473 *   #include "include/random_permeability.h"
474 *   #include "include/random_darcy.h"
475 *   #include "include/mlmc.h"
476 *  
477 *   #include <fstream>
478 *   #include <iostream>
479 *  
480 *   int main()
481 *   {
482 *   using namespace dealii;
483 *  
484 *   std::ofstream outfile("mlmc_results.txt");
485 *   if (!outfile.is_open()) {
486 *   std::cerr << "Error: Could not open mlmc_results.txt for writing!" << std::endl;
487 *   return 1;
488 *   }
489 *  
490 * @endcode
491 *
492 * Run parameters
493 *
494 * @code
495 *   double domain_length = 10.0;
496 *  
497 *   /* Describes how strongly different points are correlated.
498 *   A higher correlation length means the random field is more "spread out."
499 *   A shorter correlation length increases local variance, which requires more KL terms
500 *   to capture the higher frequencies. This is similar to a Fourier series, where a
501 *   function with high-frequency oscillations requires more terms for a sufficient approximation. */
502 *   double correlation_length = 5.0;
503 *  
504 * @endcode
505 *
506 * The number of terms chosen for 1D. For higher dimensions, we use oneD_samples^dim.
507 *
508 * @code
509 *   unsigned int oneD_samples = 20;
510 *  
511 * @endcode
512 *
513 * The number of levels was not chosen arbitrarily. Convergence is usually so fast
514 * that using more than 4–5 levels is rarely necessary.
515 *
516 * @code
517 *   unsigned int levels = 5;
518 *  
519 * @endcode
520 *
521 * We used a constant mean here. While this was a specific choice for this case,
522 * in practice, most mean functions are spatially varying rather than constant.
523 *
524 * @code
525 *   double mu = 1.0;
526 *  
527 *   /* This tolerance should not be confused with the tolerances seen in standard numerical
528 *   convergence studies or linear solvers. Those low tolerances (on the order of 1e-8)
529 *   would be realistically unachievable here; instead, we use a more moderate tolerance
530 *   that keeps the error "small enough." A typical value used in research is 1e-2 to 1e-4
531 *   at most. A larger value was chosen here for demonstration purposes, but the results
532 *   are still quite acceptable. Note that this value is squared later, so the actual
533 *   tolerance used to abort the runs is smaller. */
534 *   double tolerance = 1e-1;
535 *  
536 * @endcode
537 *
538 * In general, these values should be determined by a pilot run.
539 * I chose these specific values to keep the implementation simple.
540 *
541 * @code
542 *   std::vector<unsigned int> runs_per_level{20000, 10000, 5000, 2500, 1000, 250};
543 *  
544 *   MultilevelMonteCarlo::MLMC<2> mlmc(oneD_samples);
545 *   std::vector<double> first_sample{};
546 *  
547 *   /* The construction of this class also computes the KL expansion.
548 *   We compute the expansion once and then reuse it by swapping out the samples. */
549 *   RandomField::RandomPermeability<2> permeability(first_sample, oneD_samples, domain_length, correlation_length, mu);
550 *  
551 *   Discretization::RandomDarcy<2> random_darcy;
552 *  
553 *   random_darcy.generate_mesh(domain_length);
554 *  
555 *   double global_mean = 0.0;
556 *  
557 *  
558 *  
559 *   for(unsigned int i = 0; i<=levels; i++)
560 *   {
561 *   std::cout << "Level:" << i << std::endl;
562 *   for(unsigned int j = 0; j<runs_per_level[i]; j++)
563 *   {
564 *   permeability.overwrite_samples(mlmc.generate_samples());
565 *  
566 * @endcode
567 *
568 * coarse run
569 *
570 * @code
571 *   random_darcy.set_tria(false);
572 *   random_darcy.setup_system();
573 *   random_darcy.assemble_system(permeability);
574 *   random_darcy.solve();
575 *   double Keff_coarse = random_darcy.compute_Keff(permeability);
576 *   if( i == 0)
577 *   {
578 *   mlmc.add_sample(Keff_coarse);
579 *   }
580 *  
581 *  
582 * @endcode
583 *
584 * fine run
585 *
586 * @code
587 *   if(i!=0)
588 *   {
589 *   random_darcy.set_tria(true);
590 *   random_darcy.setup_system();
591 *   random_darcy.assemble_system(permeability);
592 *   random_darcy.solve();
593 *   double Keff_fine = random_darcy.compute_Keff(permeability);
594 *  
595 *   mlmc.add_sample(Keff_fine-Keff_coarse);
596 *  
597 *   }
598 *  
599 *   if( j >=2)
600 *   {
601 *   double var = mlmc.compute_variance();
602 *  
603 *   if (var / (j+1) < tolerance * tolerance)
604 *   {
605 *   global_mean+= mlmc.compute_mean();
606 *   std::cout << "number of samples for level" << i << ":" << j << std::endl;
607 *   random_darcy.output_results(i==0, i);
608 *  
609 *   outfile << "FINAL_STATS Level:" << i << " Mean:" << mlmc.compute_mean()
610 *   << " Var:" << mlmc.compute_variance() << " Samples:" << j << std::endl;
611 *   break;
612 *   }
613 *   }
614 *  
615 *  
616 *   }
617 *   random_darcy.refine_grid(i==0);
618 *   mlmc.clear_samples();
619 *   std::cout <<"global mean" << global_mean << std::endl;
620 *   }
621 *  
622 *   outfile.close();
623 *  
624 *  
625 *   return 0;
626 *   }
627 *  
628 * @endcode
629
630
631<a name="ann-source/KL_expansion.cc"></a>
632<h1>Annotated version of source/KL_expansion.cc</h1>
633 *
634 *
635 *
636 *
637 * @code
638 *   /* -----------------------------------------------------------------------------
639 *   *
640 *   * SPDX-License-Identifier: LGPL-2.1-or-later
641 *   * Copyright (C) 2026 by Jonas Plank
642 *   *
643 *   * This file is part of the deal.II code gallery.
644 *   *
645 *   * -----------------------------------------------------------------------------
646 *   */
647 *   #include "../include/KL_expansion.h"
648 *   #include <cmath>
649 *   #include <math.h>
650 *  
651 *   template <int dim>
652 *   RandomField::KLExpansion<dim>::KLExpansion(const unsigned int n_terms, double domain_length, double correlation_length, double mu)
653 *   : n_terms_(n_terms)
654 *   , domain_length_(domain_length)
655 *   , correlation_length_(correlation_length)
656 *   , mu_(mu)
657 *   {
658 *   compute_omega_i();
659 *   compute_alpha_i();
660 *   compute_lambda_i();
661 *   }
662 *  
663 *   template <int dim>
664 *   double RandomField::KLExpansion<dim>::compute_kl_expansion(const Point<dim>& p, std::vector<double> &samples)
665 *   {
666 *   double val = mu_;
667 *   if constexpr (dim == 1)
668 *   {
669 *   for(unsigned int i = 0; i<n_terms_; i++)
670 *   {
671 * @endcode
672 *
673 * even
674 *
675 * @code
676 *   if((i+1)%2 == 0)
677 *   {
678 *   val += std::sqrt(lambda_i[i])*samples[i]*alpha_i[i]*std::sin(omega_i[i]*(p[0]-domain_length_/2));
679 *   }
680 * @endcode
681 *
682 * odd
683 *
684 * @code
685 *   else
686 *   {
687 *   val += std::sqrt(lambda_i[i])*samples[i]*alpha_i[i]*std::cos(omega_i[i]*(p[0]-domain_length_/2));
688 *   }
689 *   }
690 *   }
691 *   if constexpr (dim == 2)
692 *   {
693 *   for(unsigned int i = 0; i<n_terms_; i++)
694 *   {
695 *   for(unsigned int j = 0; j<n_terms_; j++)
696 *   {
697 * @endcode
698 *
699 * both even
700 *
701 * @code
702 *   if((i+1)%2==0 && (j+1)%2==0)
703 *   {
704 *   val+=std::sqrt(lambda_i[i]*lambda_i[j])*samples[i*n_terms_+j]*alpha_i[i]*alpha_i[j]*std::sin(omega_i[i]*(p[0]-domain_length_/2))*std::sin(omega_i[j]*(p[1]-domain_length_/2));
705 *   }
706 * @endcode
707 *
708 * both odd
709 *
710 * @code
711 *   else if((i+1)%2==1 && (j+1)%2==1)
712 *   {
713 *   val+=std::sqrt(lambda_i[i]*lambda_i[j])*samples[i*n_terms_+j]*alpha_i[i]*alpha_i[j]*std::cos(omega_i[i]*(p[0]-domain_length_/2))*std::cos(omega_i[j]*(p[1]-domain_length_/2));
714 *   }
715 * @endcode
716 *
717 * i odd
718 *
719 * @code
720 *   else if((i+1)%2==1 && (j+1)%2==0)
721 *   {
722 *   val+=std::sqrt(lambda_i[i]*lambda_i[j])*samples[i*n_terms_+j]*alpha_i[i]*alpha_i[j]*std::cos(omega_i[i]*(p[0]-domain_length_/2))*std::sin(omega_i[j]*(p[1]-domain_length_/2));
723 *   }
724 * @endcode
725 *
726 * j odd
727 *
728 * @code
729 *   else if((i+1)%2==0 && (j+1)%2==1)
730 *   {
731 *   val+=std::sqrt(lambda_i[i]*lambda_i[j])*samples[i*n_terms_+j]*alpha_i[i]*alpha_i[j]*std::sin(omega_i[i]*(p[0]-domain_length_/2))*std::cos(omega_i[j]*(p[1]-domain_length_/2));
732 *   }
733 *  
734 *   }
735 *   }
736 *   }
737 *  
738 *   return val;
739 *   }
740 *  
741 *   template <int dim>
742 *   void RandomField::KLExpansion<dim>::compute_omega_i()
743 *   {
744 *   for(unsigned int i = 1; i<=n_terms_; i++)
745 *   {
746 *   if(i%2 == 0)
747 *   {
748 *   newton_even(i);
749 *   }
750 *   else
751 *   {
752 *   newton_odd(i);
753 *   }
754 *   }
755 *   }
756 *  
757 *   template <int dim>
758 *   void RandomField::KLExpansion<dim>::compute_alpha_i()
759 *   {
760 *   for(unsigned int i = 1; i<=n_terms_; i++)
761 *   {
762 *   if(i%2 == 0)
763 *   {
764 *   double w_i = omega_i[i-1];
765 *   double sqrt_val = domain_length_/2 -std::sin(w_i*domain_length_)/(2*w_i);
766 *   alpha_i.push_back(1/(std::sqrt(sqrt_val)));
767 *   }
768 *   else
769 *   {
770 *   double w_i = omega_i[i-1];
771 *   double sqrt_val = domain_length_/2 +std::sin(w_i*domain_length_)/(2*w_i);
772 *   alpha_i.push_back(1/(std::sqrt(sqrt_val)));
773 *   }
774 *   }
775 *   }
776 *  
777 *   template <int dim>
778 *   void RandomField::KLExpansion<dim>::compute_lambda_i()
779 *   {
780 *   for(unsigned int i = 1; i<=n_terms_; i++)
781 *   {
782 *   double w_i = omega_i[i-1];
783 *   double lambda = 2*correlation_length_/(1+w_i*w_i*correlation_length_*correlation_length_);
784 *   lambda_i.push_back(lambda);
785 *   }
786 *  
787 *   }
788 *  
789 *   template <int dim>
790 *   void RandomField::KLExpansion<dim>::newton_even(unsigned int index)
791 *   {
792 *   double a = M_PI / domain_length_ * (index - 1);
793 *   double b = M_PI / domain_length_ * index;
794 *  
795 *   double x = a + 0.1 * (b - a);
796 *  
797 *   for (int i = 0; i < 50; ++i)
798 *   {
799 *   double fx = f_even(x);
800 *   double dfx = grad_f_even(x);
801 *  
802 *   if (std::abs(dfx) < 1e-14) break;
803 *  
804 *   double step = fx / dfx;
805 *   double x_new = x - step;
806 *  
807 *   int backtrack_count = 0;
808 *   while ((x_new <= a || x_new >= b) && backtrack_count < 10)
809 *   {
810 *   step *= 0.5;
811 *   x_new = x - step;
812 *   backtrack_count++;
813 *   }
814 *  
815 *   if (x_new <= a || x_new >= b) break;
816 *  
817 *  
818 *   if (!std::isfinite(x_new)) break;
819 *   if (std::abs(x_new - x) < 1e-12)
820 *   {
821 *   x = x_new;
822 *   break;
823 *   }
824 *  
825 *   x = x_new;
826 *   }
827 *   omega_i.push_back(x);
828 *   }
829 *  
830 *   template <int dim>
831 *   void RandomField::KLExpansion<dim>::newton_odd(unsigned int index)
832 *   {
833 *   double x = M_PI / domain_length_ * (static_cast<double>(index) - 0.2);
834 *  
835 *   for (int i = 0; i < 50; ++i)
836 *   {
837 *   double fx = f_odd(x);
838 *   double dfx = grad_f_odd(x);
839 *  
840 *   if (std::abs(dfx) < 1e-12) break;
841 *  
842 *   double x_new = x - fx / dfx;
843 *  
844 *   if (std::abs(x_new - x) < 1e-10) break;
845 *   if (!std::isfinite(x_new)) break;
846 *  
847 *   x = x_new;
848 *   }
849 *   omega_i.push_back(x);
850 *   }
851 *  
852 *  
853 *   template <int dim>
854 *   double RandomField::KLExpansion<dim>::f_odd(double x)
855 *   {
856 *   return 1.0 / correlation_length_ - x * std::tan(x * domain_length_ / 2.0);
857 *   }
858 *  
859 *   template <int dim>
860 *   double RandomField::KLExpansion<dim>::f_even(double x)
861 *   {
862 *   return (1.0 / correlation_length_) * std::tan(x * domain_length_ / 2.0) + x;
863 *   }
864 *  
865 *   template <int dim>
866 *   double RandomField::KLExpansion<dim>::grad_f_odd(double x)
867 *   {
868 *   const double L = domain_length_;
869 *   const double t = std::tan(x * L / 2.0);
870 *   const double sec2 = 1.0 / std::cos(x * L / 2.0);
871 *   return -t - x * (L / 2.0) * sec2 * sec2;
872 *   }
873 *  
874 *   template <int dim>
875 *   double RandomField::KLExpansion<dim>::grad_f_even(double x)
876 *   {
877 *   const double L = domain_length_;
878 *   const double sec2 = 1.0 / std::cos(x * L / 2.0);
879 *   return (1.0 / correlation_length_) * (L / 2.0) * sec2 * sec2 + 1.0;
880 *   }
881 *  
882 *   template class RandomField::KLExpansion<1>;
883 *   template class RandomField::KLExpansion<2>;
884 *  
885 *  
886 * @endcode
887
888
889<a name="ann-source/mlmc.cc"></a>
890<h1>Annotated version of source/mlmc.cc</h1>
891 *
892 *
893 *
894 *
895 * @code
896 *   /* -----------------------------------------------------------------------------
897 *   *
898 *   * SPDX-License-Identifier: LGPL-2.1-or-later
899 *   * Copyright (C) 2026 by Jonas Plank
900 *   *
901 *   * This file is part of the deal.II code gallery.
902 *   *
903 *   * -----------------------------------------------------------------------------
904 *   */
905 *  
906 *   #include "../include/mlmc.h"
907 *   #include <cmath>
908 *  
909 *   template <int dim>
910 *   MultilevelMonteCarlo::MLMC<dim>::MLMC(unsigned int oneD_samples)
911 *   : rng(std::random_device{}())
912 *   , dist(0.0, 1.0) // mean = 0, stddev = 1
913 *   {
914 *   if constexpr (dim == 1) num_samples = oneD_samples;
915 *   else if constexpr (dim == 2) num_samples = oneD_samples*oneD_samples;
916 *   }
917 *  
918 *   template <int dim>
919 *   std::vector<double> MultilevelMonteCarlo::MLMC<dim>::generate_samples()
920 *   {
921 *   std::vector<double> samples(num_samples);
922 *  
923 *   for (auto &s : samples)
924 *   {
925 *   s = dist(rng);
926 *   }
927 *   return samples;
928 *   }
929 *  
930 *   template<int dim>
931 *   void MultilevelMonteCarlo::MLMC<dim>::add_sample(double rvalue)
932 *   {
933 *   results.push_back(rvalue);
934 *   }
935 *  
936 *   template <int dim>
937 *   double MultilevelMonteCarlo::MLMC<dim>::compute_mean()
938 *   {
939 *   double mean = 0.0;
940 *   for(unsigned int i = 0; i<results.size(); i++)
941 *   {
942 *   mean+=results[i];
943 *   }
944 *   return mean/results.size();
945 *   }
946 *  
947 *   template <int dim>
948 *   double MultilevelMonteCarlo::MLMC<dim>::compute_variance()
949 *   {
950 *   double mean = compute_mean();
951 *   double var = 0.0;
952 *   for(unsigned int i = 0; i<results.size(); i++)
953 *   {
954 *   var += std::pow((results[i]-mean),2);
955 *   }
956 *  
957 *   return var / (results.size() - 1);
958 *   }
959 *  
960 *   template <int dim>
961 *   void MultilevelMonteCarlo::MLMC<dim>::clear_samples()
962 *   {
963 *   results.clear();
964 *   }
965 *  
966 *   template class MultilevelMonteCarlo::MLMC<1>;
967 *   template class MultilevelMonteCarlo::MLMC<2>;
968 *  
969 * @endcode
970
971
972<a name="ann-source/random_darcy.cc"></a>
973<h1>Annotated version of source/random_darcy.cc</h1>
974 *
975 *
976 *
977 *
978 * @code
979 *   /* -----------------------------------------------------------------------------
980 *   *
981 *   * SPDX-License-Identifier: LGPL-2.1-or-later
982 *   * Copyright (C) 2026 by Jonas Plank
983 *   *
984 *   * This file is part of the deal.II code gallery.
985 *   *
986 *   * -----------------------------------------------------------------------------
987 *   */
988 *  
989 *   #include "../include/random_darcy.h"
990 *   #include <deal.II/lac/sparse_direct.h>
991 *  
992 *   template <int dim>
993 *   Discretization::RandomDarcy<dim>::RandomDarcy()
994 *   : fe(1)
995 *   , dof_handler(coarse_tria)
996 *   {}
997 *  
998 *   template <int dim>
999 *   void Discretization::RandomDarcy<dim>::set_tria(bool fine)
1000 *   {
1001 *   if(fine)
1002 *   {
1003 *   dof_handler.reinit(fine_tria);
1004 *   }
1005 *   else
1006 *   {
1007 *   dof_handler.reinit(coarse_tria);
1008 *   }
1009 *   }
1010 *  
1011 *   template <int dim>
1012 *   void Discretization::RandomDarcy<dim>::generate_mesh(double domain_length)
1013 *   {
1014 *   GridGenerator::hyper_cube(coarse_tria, 0, domain_length, true);
1015 *   GridGenerator::hyper_cube(fine_tria, 0, domain_length, true);
1016 *  
1017 *   coarse_tria.refine_global(3);
1018 *   fine_tria.refine_global(3);
1019 *   }
1020 *  
1021 *   template <int dim>
1022 *   void Discretization::RandomDarcy<dim>::setup_system()
1023 *   {
1024 *   dof_handler.distribute_dofs(fe);
1025 *  
1026 *   solution.reinit(dof_handler.n_dofs());
1027 *   system_rhs.reinit(dof_handler.n_dofs());
1028 *  
1029 *   constraints.clear();
1030 *   DoFTools::make_hanging_node_constraints(dof_handler, constraints);
1031 *  
1032 * @endcode
1033 *
1034 * 1. Left boundary (indicator 0): u = 1.0
1035 *
1036 * @code
1038 *   0,
1039 *   Functions::ConstantFunction<dim>(1.0),
1040 *   constraints);
1041 *  
1042 * @endcode
1043 *
1044 * 2. Right boundary (indicator 1): u = 0.0
1045 *
1046 * @code
1048 *   1,
1049 *   Functions::ConstantFunction<dim>(0.0),
1050 *   constraints);
1051 *  
1052 *   constraints.close();
1053 *  
1054 *   DynamicSparsityPattern dsp(dof_handler.n_dofs());
1055 *   DoFTools::make_sparsity_pattern(dof_handler,
1056 *   dsp,
1057 *   constraints,
1058 *   /*keep_constrained_dofs = */ false);
1059 *  
1060 *   sparsity_pattern.copy_from(dsp);
1061 *  
1062 *   system_matrix.reinit(sparsity_pattern);
1063 *   }
1064 *  
1065 *   template <int dim>
1066 *   void Discretization::RandomDarcy<dim>::assemble_system(RandomField::RandomPermeability<dim>& permeability)
1067 *   {
1068 *   const QGauss<dim> quadrature_formula(fe.degree + 1);
1069 *  
1070 *   FEValues<dim> fe_values(fe,
1071 *   quadrature_formula,
1074 *  
1075 *   const unsigned int dofs_per_cell = fe.n_dofs_per_cell();
1076 *  
1077 *   FullMatrix<double> cell_matrix(dofs_per_cell, dofs_per_cell);
1078 *   Vector<double> cell_rhs(dofs_per_cell);
1079 *  
1080 *   std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell);
1081 *  
1082 *   for (const auto &cell : dof_handler.active_cell_iterators())
1083 *   {
1084 *   fe_values.reinit(cell);
1085 *  
1086 *   cell_matrix = 0;
1087 *   cell_rhs = 0;
1088 *  
1089 *   for (const unsigned int q_index : fe_values.quadrature_point_indices())
1090 *   {
1091 *   const double current_coefficient =
1092 *   permeability.value(fe_values.quadrature_point(q_index));
1093 *   for (const unsigned int i : fe_values.dof_indices())
1094 *   {
1095 *   for (const unsigned int j : fe_values.dof_indices())
1096 *   cell_matrix(i, j) +=
1097 *   (current_coefficient * // a(x_q)
1098 *   fe_values.shape_grad(i, q_index) * // grad phi_i(x_q)
1099 *   fe_values.shape_grad(j, q_index) * // grad phi_j(x_q)
1100 *   fe_values.JxW(q_index)); // dx
1101 *  
1102 *   cell_rhs(i) += (fe_values.shape_value(i, q_index) * // phi_i(x_q)
1103 *   1.0 * // f(x)
1104 *   fe_values.JxW(q_index)); // dx
1105 *   }
1106 *   }
1107 *  
1108 *   cell->get_dof_indices(local_dof_indices);
1109 *   constraints.distribute_local_to_global(
1110 *   cell_matrix, cell_rhs, local_dof_indices, system_matrix, system_rhs);
1111 *   }
1112 *  
1113 *   }
1114 *  
1115 *   template <int dim>
1116 *   void Discretization::RandomDarcy<dim>::solve()
1117 *   {
1118 *   SparseDirectUMFPACK A_direct;
1119 *  
1120 *   solution = system_rhs;
1121 *   A_direct.solve(system_matrix, solution);
1122 *  
1123 *   constraints.distribute(solution);
1124 *   }
1125 *  
1126 *   template <int dim>
1127 *   void Discretization::RandomDarcy<dim>::refine_grid(bool firstRun)
1128 *   {
1129 *   fine_tria.refine_global(1);
1130 *   if(!firstRun)
1131 *   {
1132 *   coarse_tria.refine_global(1);
1133 *   }
1134 *   }
1135 *  
1136 *   template <int dim>
1137 *   void Discretization::RandomDarcy<dim>::output_results(bool firstRun, unsigned int level)
1138 *   {
1139 *   DataOut<dim> data_out;
1140 *  
1141 *   data_out.attach_dof_handler(dof_handler);
1142 *   data_out.add_data_vector(solution, "solution");
1143 *  
1144 *   data_out.build_patches();
1145 *  
1146 *   std::ofstream output(firstRun ? "solutionCoarse" + std::to_string(level) + ".vtk" : "solutionFine" + std::to_string(level) + ".vtk");
1147 *   data_out.write_vtk(output);
1148 *   }
1149 *  
1150 *   template <int dim>
1151 *   double Discretization::RandomDarcy<dim>::compute_Keff(RandomField::RandomPermeability<dim>& permeability)
1152 *   {
1153 *   const QGauss<dim - 1> face_quadrature_formula(fe.degree + 1);
1154 *   FEFaceValues<dim> fe_face_values(fe,
1155 *   face_quadrature_formula,
1158 *  
1159 *   std::vector<Tensor<1, dim>> solution_gradients(face_quadrature_formula.size());
1160 *   double Keff = 0.0;
1161 *  
1162 *   for (const auto &cell : dof_handler.active_cell_iterators())
1163 *   { for (const auto face_no : cell->face_indices())
1164 *   {
1165 *   if (cell->face(face_no)->at_boundary() &&
1166 *   (cell->face(face_no)->boundary_id() == 1))
1167 *   {
1168 *   fe_face_values.reinit(cell, face_no);
1169 *   fe_face_values.get_function_gradients(solution, solution_gradients);
1170 *   for (const unsigned int q_index : fe_face_values.quadrature_point_indices())
1171 *   {
1172 *   const double current_coefficient = permeability.value(fe_face_values.quadrature_point(q_index));
1173 *   Keff -= current_coefficient*solution_gradients[q_index][0]*fe_face_values.JxW(q_index);
1174 *   }
1175 *   }
1176 *  
1177 *   }
1178 *   }
1179 *  
1180 *   return Keff;
1181 *   }
1182 *  
1183 *   template class Discretization::RandomDarcy<1>;
1184 *   template class Discretization::RandomDarcy<2>;
1185 *  
1186 * @endcode
1187
1188
1189<a name="ann-source/random_permeability.cc"></a>
1190<h1>Annotated version of source/random_permeability.cc</h1>
1191 *
1192 *
1193 *
1194 *
1195 * @code
1196 *   /* -----------------------------------------------------------------------------
1197 *   *
1198 *   * SPDX-License-Identifier: LGPL-2.1-or-later
1199 *   * Copyright (C) 2026 by Jonas Plank
1200 *   *
1201 *   * This file is part of the deal.II code gallery.
1202 *   *
1203 *   * -----------------------------------------------------------------------------
1204 *   */
1205 *   #include "../include/random_permeability.h"
1206 *  
1207 *   template <int dim>
1208 *   RandomField::RandomPermeability<dim>::RandomPermeability(std::vector<double> &xi, unsigned int n_terms, double domain_length, double correlation_length, double mu)
1209 *   : kl_expansion(n_terms, domain_length, correlation_length, mu)
1210 *   , samples(xi)
1211 *   {}
1212 *  
1213 *   template <int dim>
1214 *   void RandomField::RandomPermeability<dim>::overwrite_samples(const std::vector<double> &next_sample)
1215 *   {
1216 *   samples = next_sample;
1217 *   }
1218 *  
1219 *   template <int dim>
1220 *   double RandomField::RandomPermeability<dim>::value(const Point<dim>& p)
1221 *   {
1222 *   return std::exp(kl_expansion.compute_kl_expansion(p, samples));
1223 *   }
1224 *  
1225 *   template class RandomField::RandomPermeability<1>;
1226 *   template class RandomField::RandomPermeability<2>;
1227 *  
1228 * @endcode
1229
1230
1231<a name="ann-utils/post_processing.py"></a>
1232<h1>Annotated version of utils/post_processing.py</h1>
1233@code{.py}
1234import re
1235import matplotlib.pyplot as plt
1236import numpy as np
1237import os
1238
1239def plot_from_file(filename):
1240 if not os.path.exists(filename):
1241 print(f"Error: {filename} not found. Did you run the C++ code first or is it perhaps in the wrong folder?")
1242 return
1243
1244 levels, means, variances, samples = [], [], [], []
1245
1246 pattern = re.compile(r"FINAL_STATS Level:(\d+) Mean:([\d\.e+-]+) Var:([\d\.e+-]+) Samples:(\d+)")
1247
1248 with open(filename, 'r') as f:
1249 for line in f:
1250 match = pattern.search(line)
1251 if match:
1252 levels.append(int(match.group(1)))
1253 means.append(abs(float(match.group(2)))) # Absolute for log-plot
1254 variances.append(float(match.group(3)))
1255 samples.append(int(match.group(4)))
1256
1257 if not levels:
1258 print("No valid MLMC data found in the file.")
1259 return
1260
1261 # Convert to arrays for plotting
1262 levels = np.array(levels)
1263
1264 fig, axs = plt.subplots(1, 3, figsize=(16, 5))
1265
1266 # 1. Variance Decay
1267 axs[0].plot(levels, variances, 'o-', color='firebrick', label=r'Var[@f$P_l - P_{l-1}@f$]')
1268 axs[0].set_yscale('log')
1269 axs[0].set_title('Variance Decay', fontsize=12, fontweight='bold')
1270 axs[0].set_xlabel('Level')
1271 axs[0].grid(True, which='both', alpha=0.3)
1272 axs[0].legend()
1273
1274 # 2. Mean Difference (Bias)
1275 axs[1].plot(levels, means, 's-', color='royalblue', label=r'|@f$E[P_l - P_{l-1}]@f$|')
1276 axs[1].set_yscale('log')
1277 axs[1].set_title('Mean Difference (Bias)', fontsize=12, fontweight='bold')
1278 axs[1].set_xlabel('Level')
1279 axs[1].grid(True, which='both', alpha=0.3)
1280 axs[1].legend()
1281
1282 # 3. Samples per Level
1283 axs[2].bar(levels, samples, color='seagreen', alpha=0.7)
1284 axs[2].set_yscale('log')
1285 axs[2].set_title('Samples per Level (Workload)', fontsize=12, fontweight='bold')
1286 axs[2].set_xlabel('Level')
1287 axs[2].set_ylabel('@f$N_l@f$')
1288 axs[2].grid(axis='y', alpha=0.3, linestyle='--')
1289
1290 plt.tight_layout()
1291 plt.savefig('mlmc_plots.png', dpi=150)
1292 print("Plot saved as 'mlmc_plots.png'")
1293 plt.show()
1294
1295if __name__ == "__main__":
1296 plot_from_file('mlmc_results.txt')
1297
1298 @endcode
1299
1300
1301*/
*  *  for(const auto &cell :triangulation.active_cell_iterators())
*  *  int main(int argc, char **argv)
*  *  *  struct InterferenceTaperTransform *  
void attach_dof_handler(const DoFHandler< dim, spacedim > &)
Definition fe_q.h:552
virtual RangeNumberType value(const Point< dim > &p, const unsigned int component=0) const
Definition point.h:111
void solve(Vector< double > &rhs_and_solution, const bool transpose=false) const
Point< 2 > first
Definition grid_out.cc:4639
unsigned int level
Definition grid_out.cc:4642
void make_hanging_node_constraints(const DoFHandler< dim, spacedim > &dof_handler, AffineConstraints< number > &constraints)
void make_sparsity_pattern(const DoFHandler< dim, spacedim > &dof_handler, SparsityPatternBase &sparsity_pattern, const AffineConstraints< number > &constraints={}, const bool keep_constrained_dofs=true, const types::subdomain_id subdomain_id=numbers::invalid_subdomain_id)
@ update_values
Shape function values.
@ update_normal_vectors
Normal vectors.
@ update_JxW_values
Transformed quadrature weights.
@ update_gradients
Shape function gradients.
@ update_quadrature_points
Transformed quadrature points.
void hyper_cube(Triangulation< dim, spacedim > &tria, const double left=0., const double right=1., const bool colorize=false)
void refine(Triangulation< dim, spacedim > &tria, const Vector< Number > &criteria, const double threshold, const unsigned int max_to_mark=numbers::invalid_unsigned_int)
@ general
No special properties.
constexpr char L
constexpr types::blas_int one
void cell_matrix(FullMatrix< double > &M, const FEValuesBase< dim > &fe, const FEValuesBase< dim > &fetest, const ArrayView< const std::vector< double > > &velocity, const double factor=1.)
Definition advection.h:72
double norm(const FEValuesBase< dim > &fe, const ArrayView< const std::vector< Tensor< 1, dim > > > &Du)
Definition divergence.h:469
Point< spacedim > point(const gp_Pnt &p, const double tolerance=1e-10)
Definition utilities.cc:210
std::string to_string(const T &t)
Definition patterns.h:2450
SymmetricTensor< 2, dim, Number > e(const Tensor< 2, dim, Number > &F)
SymmetricTensor< 2, dim, Number > b(const Tensor< 2, dim, Number > &F)
*  *  if(update_pressure &update_flags) *  compute_pressure(constitutive_request
*  *  *  ThermoPlasticMaterial< dim, ViscoplasticYieldLaw, Number >  ThermoPlasticMaterial *  mu(mu)
*  *  *  *  std::vector< Number > ThermoPlasticMaterial< dim, ViscoplasticYieldLaw, Number >::get_state_parameters   const
void interpolate_boundary_values(const Mapping< dim, spacedim > &mapping, const DoFHandler< dim, spacedim > &dof, const std::map< types::boundary_id, const Function< spacedim, number > * > &function_map, std::map< types::global_dof_index, number > &boundary_values, const ComponentMask &component_mask={})
void run(const Iterator &begin, const std_cxx20::type_identity_t< Iterator > &end, Worker worker, Copier copier, const ScratchData &sample_scratch_data, const CopyData &sample_copy_data, const unsigned int queue_length, const unsigned int chunk_size)
int(&) functions(const void *v1, const void *v2)
void reinit(MatrixBlock< MatrixType > &v, const BlockSparsityPattern &p)
STL namespace.
::VectorizedArray< Number, width > exp(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > tan(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > cos(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > sin(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > sqrt(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > pow(const ::VectorizedArray< Number, width > &, const Number p)
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)
unsigned int boundary_id
Definition types.h:159
std::array< Number, 1 > eigenvalues(const SymmetricTensor< 2, 1, Number > &T)