Logbook  (07-04-2025)
Static problems
project_Hcurl_to_Hdiv.hpp
1 /*****************************************************************************
2  * Copyright (C) Siarhei Uzunbajakau, 2023.
3  *
4  * This program is free software. You can use, modify, and redistribute it under
5  * the terms of the GNU Lesser General Public License as published by the Free
6  * Software Foundation, either version 3 or (at your option) any later version.
7  * This program is distributed without any warranty.
8  *
9  * Refer to COPYING.LESSER for more details.
10  ******************************************************************************/
11 
12 #ifndef ProjectHcurlToHdiv_H__
13 #define ProjectHcurlToHdiv_H__
14 
15 #define BOOST_ALLOW_DEPRECATED_HEADERS
16 
17 #include <deal.II/base/timer.h>
18 #include <deal.II/base/work_stream.h>
19 
20 #include <deal.II/grid/tria.h>
21 
22 #include <deal.II/dofs/dof_handler.h>
23 #include <deal.II/dofs/dof_renumbering.h>
24 #include <deal.II/dofs/dof_tools.h>
25 
26 #include <deal.II/lac/full_matrix.h>
27 #include <deal.II/lac/sparse_direct.h>
28 
29 #include <deal.II/lac/precondition.h>
30 #include <deal.II/lac/solver_cg.h>
31 #include <deal.II/lac/solver_control.h>
32 
33 #include <deal.II/fe/fe_raviart_thomas.h>
34 
35 #include <deal.II/fe/fe_values.h>
36 #include <deal.II/fe/mapping_q1.h>
37 
38 #include <deal.II/numerics/data_out.h>
39 #include <deal.II/numerics/matrix_tools.h>
40 #include <deal.II/numerics/vector_tools.h>
41 
42 #include <fstream>
43 #include <iomanip>
44 #include <ios>
45 #include <iostream>
46 #include <string>
47 
48 #include "constants.hpp"
49 #include "static_vector_input.hpp"
50 
51 #define VE scratch_data.ve
52 
53 #define TMR(__name) TimerOutput::Scope timer_section(timer, __name)
54 
55 using namespace dealii;
56 
57 namespace StaticVectorSolver {
58 
105 template<int stage = 1>
107 {
108 public:
109  ProjectHcurlToHdiv() = delete;
110 
148  ProjectHcurlToHdiv(unsigned int p,
149  unsigned int mapping_degree,
150  const Triangulation<3>& triangulation_Hcurl,
151  const DoFHandler<3>& dof_handler_Hcurl,
152  const Vector<double>& solution_Hcurl,
153  std::string fname = "Hdiv",
154  const Function<3>* exact_solution = nullptr,
155  bool print_time_tables = false,
156  bool project_exact_solution = false,
157  bool log_cg_convergence = false,
158  bool write_higher_order_cells = false);
159 
163  double get_L2_norm() { return L2_norm; };
164 
168  double get_Linfty_norm() { return Linfty_norm; }
169 
173  unsigned int get_n_cells() const
174  {
175  return static_cast<unsigned int>(triangulation_Hcurl.n_active_cells());
176  }
177 
181  unsigned int get_n_dofs() const
182  {
183  return static_cast<unsigned int>(dof_handler_Hdiv.n_dofs());
184  }
185 
190  void clear()
191  {
192  system_matrix.clear();
193  system_rhs.reinit(0);
194  }
195 
199  const Triangulation<3>& get_tria() const { return triangulation_Hcurl; }
200 
205  const DoFHandler<3>& get_dof_handler() const { return dof_handler_Hdiv; }
206 
210  const Vector<double>& get_solution() const { return solution_Hdiv; }
211 
227  void save_matrix_and_rhs_to_csv(std::string fname) const;
228 
229 private:
230  void setup();
231  void assemble();
232  void solve();
233  void save() const;
234  void compute_error_norms();
235  void project_exact_solution_fcn();
236 
237  const std::string fname;
238 
239  const DoFHandler<3>& dof_handler_Hcurl;
240  const Vector<double>& solution_Hcurl;
241 
242  const Triangulation<3>& triangulation_Hcurl;
243  const FE_RaviartThomas<3> fe_Hdiv;
244  DoFHandler<3> dof_handler_Hdiv;
245 
246  SparsityPattern sparsity_pattern;
247  SparseMatrix<double> system_matrix;
248 
249  Vector<double> solution_Hdiv;
250  Vector<double> system_rhs;
251 
252  Vector<double> projected_exact_solution;
253 
254  AffineConstraints<double> constraints;
255 
256  const Function<3>* exact_solution;
257 
258  const unsigned int mapping_degree;
259  const bool project_exact_solution;
260  const bool log_cg_convergence;
261  const bool write_higher_order_cells;
262 
263  Vector<double> L2_per_cell;
264  double L2_norm;
265 
266  Vector<double> Linfty_per_cell;
267  double Linfty_norm;
268 
269  // ----------------------------------------------------------------------------
270  // These structures and functions are related to the Work Stream algorithm.
271  // See article "WorkStream – A Design Pattern for Multicore-Enabled Finite
272  // Element Computations." by BRUNO TURCKSIN, MARTIN KRONBICHLER,
273  // WOLFGANG BANGERTH for more details.
274  // ----------------------------------------------------------------------------
275 
276  using IteratorTuple =
277  std::tuple<typename DoFHandler<3>::active_cell_iterator,
278  typename DoFHandler<3>::active_cell_iterator>;
279 
280  using IteratorPair = SynchronousIterators<IteratorTuple>;
281 
282  struct AssemblyScratchData
283  {
284  AssemblyScratchData(const FiniteElement<3>& fe,
285  const DoFHandler<3>& dof_handr_Hcurl,
286  const Vector<double>& dofs_Hcurl,
287  unsigned int mapping_degree);
288 
289  AssemblyScratchData(const AssemblyScratchData& scratch_data);
290 
291  MappingQ<3> mapping;
293  FEValues<3> fe_values_Hdiv;
294  FEValues<3> fe_values_Hcurl;
295 
296  const unsigned int dofs_per_cell;
297  const unsigned int n_q_points;
298 
299  std::vector<std::vector<Tensor<1, 3>>> vector_gradients;
300  Tensor<1, 3> curl_vec_in_Hcurl;
301 
302  const FEValuesExtractors::Vector ve;
303 
304  const DoFHandler<3>& dof_hand_Hcurl;
305  const Vector<double>& dofs_Hcurl;
306  };
307 
308  struct AssemblyCopyData
309  {
310  FullMatrix<double> cell_matrix;
311  Vector<double> cell_rhs;
312  std::vector<types::global_dof_index> local_dof_indices;
313  };
314 
315  void system_matrix_local(const IteratorPair& IP,
316  AssemblyScratchData& scratch_data,
317  AssemblyCopyData& copy_data);
318 
319  void copy_local_to_global(const AssemblyCopyData& copy_data);
320 
321  //-----------------------------------------------------------------------------
322  //-----------------------------------------------------------------------------
323  //-----------------------------------------------------------------------------
324 };
325 
326 template<int stage>
328  unsigned int p,
329  unsigned int mapping_degree,
330  const Triangulation<3>& triangulation_Hcurl,
331  const DoFHandler<3>& dof_handler_Hcurl,
332  const Vector<double>& solution_Hcurl,
333  std::string fname,
334  const Function<3>* exact_solution,
335  bool print_time_tables,
336  bool project_exact_solution,
337  bool log_cg_convergence,
338  bool write_higher_order_cells)
339  : fname(fname)
340  , dof_handler_Hcurl(dof_handler_Hcurl)
341  , solution_Hcurl(solution_Hcurl)
342  , triangulation_Hcurl(triangulation_Hcurl)
343  // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
344  // The public attribute fe.degree is the maximal polynomial degree of a
345  // shape function in a single coordinate direction, not the degree of
346  // the finite element. For FE_Nedelec and FE_RaviartThomas degree of
347  // the finite element is: degree_of_element = fe.degree - 1.
348  // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
349  // fe_Hdiv(dof_handler_Hcurl.get_fe().degree-1),
350  , fe_Hdiv(p)
351  , exact_solution(exact_solution)
352  , mapping_degree(mapping_degree)
353  , project_exact_solution(project_exact_solution)
354  , log_cg_convergence(log_cg_convergence)
355  , write_higher_order_cells(write_higher_order_cells)
356 {
357  TimerOutput::OutputFrequency tf =
358  (print_time_tables) ? TimerOutput::summary : TimerOutput::never;
359 
360  TimerOutput timer(std::cout, tf, TimerOutput::cpu_and_wall_times_grouped);
361 
362  {
363  TMR("Setup");
364  setup();
365  }
366  {
367  TMR("Assemble");
368  assemble();
369  }
370  {
371  TMR("Solve");
372  solve();
373  }
374 
375  if (exact_solution) {
376  {
377  TMR("Compute error norms");
378  compute_error_norms();
379  }
380 
381  if (project_exact_solution) {
382  {
383  TMR("Project exact solution");
384  project_exact_solution_fcn();
385  }
386  }
387  }
388 
389  {
390  TMR("Save");
391  save();
392  }
393 }
394 
395 template<int stage>
396 void
398 {
399  std::ofstream ofs_matrix(fname + "_matrix.csv");
400  std::ofstream ofs_rhs(fname + "_rhs.csv");
401 
402  for (unsigned int i = 0; i < system_matrix.m(); ++i) {
403  ofs_rhs << system_rhs(i);
404  if (i < (system_matrix.m() - 1))
405  ofs_rhs << "\n";
406 
407  for (unsigned int j = 0; j < system_matrix.n(); ++j) {
408  ofs_matrix << std::scientific << std::setprecision(16)
409  << system_matrix.el(i, j);
410 
411  if (j < (system_matrix.m() - 1))
412  ofs_matrix << ", ";
413  }
414  if (i < (system_matrix.m() - 1))
415  ofs_matrix << "\n";
416  }
417 
418  ofs_rhs.close();
419  ofs_matrix.close();
420 }
421 
422 template<int stage>
423 void
425 {
426  constraints.close();
427 
428  dof_handler_Hdiv.reinit(triangulation_Hcurl);
429  dof_handler_Hdiv.distribute_dofs(fe_Hdiv);
430 
431  DynamicSparsityPattern dsp(dof_handler_Hdiv.n_dofs(),
432  dof_handler_Hdiv.n_dofs());
433  DoFTools::make_sparsity_pattern(dof_handler_Hdiv, dsp, constraints, false);
434 
435  sparsity_pattern.copy_from(dsp);
436  system_matrix.reinit(sparsity_pattern);
437  solution_Hdiv.reinit(dof_handler_Hdiv.n_dofs());
438  system_rhs.reinit(dof_handler_Hdiv.n_dofs());
439 
440  if (project_exact_solution)
441  projected_exact_solution.reinit(dof_handler_Hdiv.n_dofs());
442 
443  if (exact_solution) {
444  L2_per_cell.reinit(triangulation_Hcurl.n_active_cells());
445  Linfty_per_cell.reinit(triangulation_Hcurl.n_active_cells());
446  }
447 }
448 
449 template<int stage>
450 void
451 ProjectHcurlToHdiv<stage>::assemble()
452 {
453  WorkStream::run(IteratorPair(IteratorTuple(dof_handler_Hdiv.begin_active(),
454  dof_handler_Hcurl.begin_active())),
455  IteratorPair(IteratorTuple(dof_handler_Hdiv.end(),
456  dof_handler_Hcurl.end())),
457  *this,
458  &ProjectHcurlToHdiv<stage>::system_matrix_local,
459  &ProjectHcurlToHdiv<stage>::copy_local_to_global,
460  AssemblyScratchData(
461  fe_Hdiv, dof_handler_Hcurl, solution_Hcurl, mapping_degree),
462  AssemblyCopyData());
463 }
464 
465 template<int stage>
466 ProjectHcurlToHdiv<stage>::AssemblyScratchData::AssemblyScratchData(
467  const FiniteElement<3>& fe,
468  const DoFHandler<3>& dof_hand_Hcurl,
469  const Vector<double>& dofs_Hcurl,
470  unsigned int mapping_degree)
471  : mapping(mapping_degree)
472  , qt(fe.degree - 1)
473  , fe_values_Hdiv(mapping,
474  fe,
475  QGauss<3>(qt.sim()),
476  update_values | update_quadrature_points | update_JxW_values)
477  , fe_values_Hcurl(mapping,
478  dof_hand_Hcurl.get_fe(),
479  QGauss<3>(qt.sim()),
480  update_gradients)
481  , dofs_per_cell(fe_values_Hdiv.dofs_per_cell)
482  , n_q_points(fe_values_Hdiv.get_quadrature().size())
483  , vector_gradients(n_q_points, std::vector<Tensor<1, 3>>(3))
484  , ve(0)
485  , dof_hand_Hcurl(dof_hand_Hcurl)
486  , dofs_Hcurl(dofs_Hcurl)
487 {
488 }
489 
490 template<int stage>
491 ProjectHcurlToHdiv<stage>::AssemblyScratchData::AssemblyScratchData(
492  const AssemblyScratchData& scratch_data)
493  : mapping(scratch_data.mapping.get_degree())
494  , qt(scratch_data.qt)
495  , fe_values_Hdiv(mapping,
496  scratch_data.fe_values_Hdiv.get_fe(),
497  scratch_data.fe_values_Hdiv.get_quadrature(),
498  update_values | update_quadrature_points | update_JxW_values)
499  , fe_values_Hcurl(mapping,
500  scratch_data.fe_values_Hcurl.get_fe(),
501  scratch_data.fe_values_Hcurl.get_quadrature(),
502  update_gradients)
503  , dofs_per_cell(fe_values_Hdiv.dofs_per_cell)
504  , n_q_points(fe_values_Hdiv.get_quadrature().size())
505  , vector_gradients(n_q_points, std::vector<Tensor<1, 3>>(3))
506  , ve(0)
507  , dof_hand_Hcurl(scratch_data.dof_hand_Hcurl)
508  , dofs_Hcurl(scratch_data.dofs_Hcurl)
509 {
510 }
511 
512 template<int stage>
513 void
514 ProjectHcurlToHdiv<stage>::system_matrix_local(
515  const IteratorPair& IP,
516  AssemblyScratchData& scratch_data,
517  AssemblyCopyData& copy_data)
518 {
519  // See the color box
520  // Recipe for projections from H(curl) to H(div) nr. 12 and 13
521 
522  copy_data.cell_matrix.reinit(scratch_data.dofs_per_cell,
523  scratch_data.dofs_per_cell);
524 
525  copy_data.cell_rhs.reinit(scratch_data.dofs_per_cell);
526 
527  copy_data.local_dof_indices.resize(scratch_data.dofs_per_cell);
528 
529  scratch_data.fe_values_Hdiv.reinit(std::get<0>(*IP));
530  scratch_data.fe_values_Hcurl.reinit(std::get<1>(*IP));
531 
532  scratch_data.fe_values_Hcurl.get_function_gradients(
533  scratch_data.dofs_Hcurl, scratch_data.vector_gradients);
534 
535  for (unsigned int q_index = 0; q_index < scratch_data.n_q_points; ++q_index) {
536  for (unsigned int i = 0; i < scratch_data.dofs_per_cell; ++i) {
537  for (unsigned int j = 0; j < scratch_data.dofs_per_cell; ++j) {
538  copy_data.cell_matrix(i, j) +=
539  scratch_data.fe_values_Hdiv[VE].value(i, q_index) *
540  scratch_data.fe_values_Hdiv[VE].value(j, q_index) *
541  scratch_data.fe_values_Hdiv.JxW(q_index);
542  }
543 
544  scratch_data.curl_vec_in_Hcurl[0] =
545  scratch_data.vector_gradients[q_index][2][1] -
546  scratch_data.vector_gradients[q_index][1][2];
547 
548  scratch_data.curl_vec_in_Hcurl[1] =
549  scratch_data.vector_gradients[q_index][0][2] -
550  scratch_data.vector_gradients[q_index][2][0];
551 
552  scratch_data.curl_vec_in_Hcurl[2] =
553  scratch_data.vector_gradients[q_index][1][0] -
554  scratch_data.vector_gradients[q_index][0][1];
555 
556  copy_data.cell_rhs(i) +=
557  scratch_data.curl_vec_in_Hcurl *
558  scratch_data.fe_values_Hdiv[VE].value(i, q_index) *
559  scratch_data.fe_values_Hdiv.JxW(q_index);
560  }
561  }
562 
563  std::get<0>(*IP)->get_dof_indices(copy_data.local_dof_indices);
564 }
565 
566 template<int stage>
567 void
568 ProjectHcurlToHdiv<stage>::copy_local_to_global(
569  const AssemblyCopyData& copy_data)
570 {
571  constraints.distribute_local_to_global(copy_data.cell_matrix,
572  copy_data.cell_rhs,
573  copy_data.local_dof_indices,
574  system_matrix,
575  system_rhs);
576 }
577 
578 template<int stage>
579 void
580 ProjectHcurlToHdiv<stage>::solve()
581 {
582  SolverControl control(
583  1000 * system_rhs.size(), 1e-12 * system_rhs.l2_norm(), false, false);
584 
585  if (log_cg_convergence)
586  control.enable_history_data();
587 
588  GrowingVectorMemory<Vector<double>> memory;
589  SolverCG<Vector<double>> cg(control, memory);
590 
591  PreconditionSSOR<SparseMatrix<double>> preconditioner;
592  preconditioner.initialize(system_matrix, 1.2);
593 
594  cg.solve(system_matrix, solution_Hdiv, system_rhs, preconditioner);
595 
596  if (log_cg_convergence) {
597  const std::vector<double> history_data = control.get_history_data();
598 
599  std::ofstream ofs(fname + "_cg_convergence.csv");
600 
601  unsigned int i = 1;
602  for (auto item : history_data) {
603  ofs << i << ", " << item << "\n";
604  i++;
605  }
606  ofs.close();
607  }
608 }
609 
610 template<int stage>
611 void
612 ProjectHcurlToHdiv<stage>::save() const
613 {
614  std::vector<std::string> solution_names(3, "VectorField");
615  std::vector<DataComponentInterpretation::DataComponentInterpretation>
616  interpretation(3, DataComponentInterpretation::component_is_part_of_vector);
617 
618  DataOut<3> data_out;
619 
620  data_out.add_data_vector(
621  dof_handler_Hdiv, solution_Hdiv, solution_names, interpretation);
622 
623  if (project_exact_solution) {
624  std::vector<std::string> solution_names_ex(3, "VectorFieldExact");
625 
626  data_out.add_data_vector(dof_handler_Hdiv,
627  projected_exact_solution,
628  solution_names_ex,
629  interpretation);
630  }
631 
632  if (exact_solution) {
633  data_out.add_data_vector(L2_per_cell, "L2norm");
634  data_out.add_data_vector(Linfty_per_cell, "LinftyNorm");
635  }
636 
637  std::ofstream ofs;
638 
639  if (write_higher_order_cells) {
640  DataOutBase::VtkFlags flags;
641  flags.write_higher_order_cells = true;
642  data_out.set_flags(flags);
643 
644  const MappingQ<3> mapping(mapping_degree);
645 
646  data_out.build_patches(mapping,
647  fe_Hdiv.degree + 2,
648  DataOut<3>::CurvedCellRegion::curved_inner_cells);
649 
650  ofs.open(fname + ".vtu");
651  data_out.write_vtu(ofs);
652 
653  } else {
654 
655  data_out.build_patches();
656 
657  ofs.open(fname + ".vtk");
658  data_out.write_vtk(ofs);
659  }
660 
661  ofs.close();
662 }
663 
664 template<int stage>
665 void
666 ProjectHcurlToHdiv<stage>::compute_error_norms()
667 {
668  Weight<3, stage> weight;
669  const Function<3, double>* mask = &weight;
670 
671  Constants::QuadratureTableVector<3> qt(dof_handler_Hdiv.get_fe().degree - 1);
672 
673  VectorTools::integrate_difference(MappingQ<3>(mapping_degree),
674  dof_handler_Hdiv,
675  solution_Hdiv,
676  *exact_solution,
677  L2_per_cell,
678  QGauss<3>(qt.enorm()),
679  VectorTools::L2_norm,
680  mask // & B_mask
681  );
682 
683  L2_norm = VectorTools::compute_global_error(
684  triangulation_Hcurl, L2_per_cell, VectorTools::L2_norm);
685 
686  VectorTools::integrate_difference(MappingQ<3>(mapping_degree),
687  dof_handler_Hdiv,
688  solution_Hdiv,
689  *exact_solution,
690  Linfty_per_cell,
691  QGauss<3>(1),
692  VectorTools::Linfty_norm,
693  mask // & B_mask
694  );
695 
696  Linfty_norm = Linfty_per_cell.linfty_norm();
697 }
698 
699 template<int stage>
700 void
701 ProjectHcurlToHdiv<stage>::project_exact_solution_fcn()
702 {
703  Constants::QuadratureTableVector<3> qt(fe_Hdiv.degree - 1);
704 
705  AffineConstraints<double> constraints_empty;
706  constraints_empty.close();
707 
708  VectorTools::project(MappingQ<3>(mapping_degree),
709  dof_handler_Hdiv,
710  constraints_empty,
711  QGauss<3>(qt.sim()),
712  *exact_solution,
713  projected_exact_solution);
714 }
715 
716 } // namespace StaticVectorSolver
717 
718 #endif
unsigned int get_n_cells() const
Returns the number of active cells in the mesh.
const Triangulation< 3 > & get_tria() const
Returns a reference to triangulation.
const Vector< double > & get_solution() const
Returns a reference to solution, i.e., the result of the projection.
void save_matrix_and_rhs_to_csv(std::string fname) const
Saves the system matrix and the right-hand side into a csv file.
unsigned int get_n_dofs() const
Returns the total amount of the degrees of freedom.
const DoFHandler< 3 > & get_dof_handler() const
Returns a reference to dof handler associated with the Raviart-Thomas finite elements.
void clear()
Releases computer memory associated with system matrix and right-hand side.
ProjectHcurlToHdiv(unsigned int p, unsigned int mapping_degree, const Triangulation< 3 > &triangulation_Hcurl, const DoFHandler< 3 > &dof_handler_Hcurl, const Vector< double > &solution_Hcurl, std::string fname="Hdiv", const Function< 3 > *exact_solution=nullptr, bool print_time_tables=false, bool project_exact_solution=false, bool log_cg_convergence=false, bool write_higher_order_cells=false)
The only constructor.