Logbook  (07-04-2025)
Static problems
static_scalar_solver.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 StaticScalarSolver_H__
13 #define StaticScalarSolver_H__
14 
15 #include <deal.II/base/exceptions.h>
16 #include <deal.II/base/function.h>
17 #include <deal.II/base/thread_management.h>
18 #include <deal.II/base/timer.h>
19 #include <deal.II/base/types.h>
20 #include <deal.II/base/work_stream.h>
21 
22 #include <deal.II/grid/tria.h>
23 
24 #include <deal.II/dofs/dof_handler.h>
25 #include <deal.II/dofs/dof_tools.h>
26 
27 #include <deal.II/fe/fe_q.h>
28 #include <deal.II/fe/fe_values.h>
29 #include <deal.II/fe/mapping_q.h>
30 
31 #include <deal.II/lac/affine_constraints.h>
32 #include <deal.II/lac/dynamic_sparsity_pattern.h>
33 #include <deal.II/lac/precondition.h>
34 #include <deal.II/lac/solver_cg.h>
35 #include <deal.II/lac/solver_control.h>
36 #include <deal.II/lac/sparse_matrix.h>
37 #include <deal.II/lac/vector.h>
38 
39 #include <deal.II/numerics/data_out.h>
40 #include <deal.II/numerics/vector_tools.h>
41 #include <deal.II/numerics/vector_tools_common.h>
42 #include <deal.II/numerics/vector_tools_project.h>
43 
44 #include <fstream>
45 #include <iomanip>
46 #include <ios>
47 #include <iostream>
48 #include <map>
49 
50 #include "constants.hpp"
51 #include "static_scalar_input.hpp"
52 
53 #define TMR(__name) TimerOutput::Scope timer_section(timer, __name)
54 
55 using namespace dealii;
56 
57 namespace StaticScalarSolver {
58 
235 template<int dim, int stage = 1>
236 class Solver
237 {
238 public:
239  Solver() = delete;
273  Solver(unsigned int p,
274  unsigned int mapping_degree,
275  unsigned int type_of_pde_rhs,
276  std::string fname = "data",
277  const Function<dim>* exact_solution = nullptr,
278  bool axisymmetric = false,
279  bool vector_potential = false,
280  bool print_time_tables = false,
281  bool project_exact_solution = false,
282  bool write_higher_order_cells = false)
283  : fe(p)
284  , mapping_degree(mapping_degree)
285  , type_of_pde_rhs(type_of_pde_rhs)
286  , fname(fname)
287  , exact_solution(exact_solution)
288  , axisymmetric(axisymmetric)
289  , vector_potential(vector_potential)
290  , print_time_tables(print_time_tables)
291  , project_exact_solution(project_exact_solution)
292  , write_higher_order_cells(write_higher_order_cells)
293  {
294  Assert(((dim == 2) || (dim == 3)), ExcInternalError());
295  Assert(p < 6, ExcInternalError());
296  Assert(type_of_pde_rhs < 4, ExcInternalError());
297 
298  if (axisymmetric) {
299  Assert(
300  dim == 2,
301  ExcMessage("The setting axisymmetric=true is only allowed if dim=2."));
302 
303  Assert(
304  type_of_pde_rhs < 2,
305  ExcMessage(
306  "The settings axisymmetric=true and type_of_pde_rhs>1 (corresponds to \
307 the modes for computing the current vector potential) are not compatible. \
308 An out-of-plane scalar current vector potential implies that the free-current \
309 density is an in-plane vector. Strictly speaking, it is impossible to setup a \
310 curl-curl equation on an axisymmetric problem domain if the free-current density \
311 is an in-plane vector. Charge conservation will fail."));
312  }
313 
314  if (vector_potential) {
315  Assert(dim == 2,
316  ExcMessage(
317  "The setting vector_potential=true can only be used if dim=2."));
318  Assert(((type_of_pde_rhs == 2) || (type_of_pde_rhs == 3)),
319  ExcMessage("The setting vector_potential=true can only be used if \
320 type_of_pde_rhs=2 or type_of_pde_rhs=3."));
321  }
322 
323  if ((type_of_pde_rhs == 0) || (type_of_pde_rhs == 1)) {
324  Assert(!vector_potential,
325  ExcMessage(
326  "The settings type_of_pde_rhs=0 and type_of_pde_rhs=1 can only \
327 be used if vector_potential=false."));
328  }
329 
330  if ((type_of_pde_rhs == 2) || (type_of_pde_rhs == 3)) {
331  Assert(dim == 2,
332  ExcMessage(
333  "The settings type_of_pde_rhs=2 and type_of_pde_rhs=3 can only \
334 be used if dim=2."));
335  Assert(vector_potential,
336  ExcMessage(
337  "The settings type_of_pde_rhs=2 and type_of_pde_rhs=3 can only \
338 be used if vector_potential=true"));
339  }
340  }
341 
353  virtual void make_mesh() = 0;
354 
384  virtual void fill_dirichlet_stack() = 0;
385 
389  virtual void solve() = 0;
390 
400  void setup();
401 
405  void assemble();
406 
411 
423 
458  void save() const;
459 
475  void save_matrix_and_rhs_to_csv(std::string fname) const;
476 
481  void clear()
482  {
483  system_matrix.clear();
484  system_rhs.reinit(0);
485  }
486 
490  const Triangulation<dim>& get_tria() const { return triangulation; }
491 
495  const DoFHandler<dim>& get_dof_handler() const { return dof_handler; }
496 
500  const Vector<double>& get_solution() const { return solution; }
501 
505  unsigned int get_n_cells() const
506  {
507  return static_cast<unsigned int>(triangulation.n_active_cells());
508  }
509 
513  unsigned int get_n_vertices() const
514  {
515  return static_cast<unsigned int>(triangulation.n_vertices());
516  }
517 
521  unsigned int get_n_used_vertices() const
522  {
523  return static_cast<unsigned int>(triangulation.n_used_vertices());
524  }
525 
529  unsigned int get_n_lines() const
530  {
531  return static_cast<unsigned int>(triangulation.n_lines());
532  }
533 
537  unsigned int get_n_dofs() const
538  {
539  return static_cast<unsigned int>(dof_handler.n_dofs());
540  }
541 
545  unsigned int get_type_of_pde_rhs() const { return type_of_pde_rhs; }
546 
550  double get_L2_norm() const { return L2_norm; }
551 
555  double get_H1_norm() const { return H1_norm; }
556 
560  double get_Linfty_norm() const { return Linfty_norm; }
561 
566  unsigned int get_mapping_degree() const { return mapping_degree; }
567 
575  void run()
576  {
577  TimerOutput::OutputFrequency tf =
578  (print_time_tables) ? TimerOutput::summary : TimerOutput::never;
579 
580  TimerOutput timer(std::cout, tf, TimerOutput::cpu_and_wall_times_grouped);
581 
582  {
583  TMR("Make mesh");
584  make_mesh();
585  }
586  {
587  TMR("Fill Dirichlet stack");
588  fill_dirichlet_stack();
589  }
590  {
591  TMR("Setup");
592  setup();
593  }
594  {
595  TMR("Assemble");
596  assemble();
597  }
598  {
599  TMR("Solve");
600  solve();
601  }
602 
603  if (exact_solution) {
604  if (project_exact_solution) {
605  TMR("Project exact solution");
606  project_exact_solution_fcn();
607  }
608 
609  {
610  TMR("Compute error norms");
611  compute_error_norms();
612  }
613  }
614 
615  {
616  TMR("Save");
617  save();
618  }
619  };
620 
621  virtual ~Solver() = default;
622 
623 protected:
632  std::map<types::boundary_id, const Function<dim>*> dirichlet_stack;
633 
637  Triangulation<dim> triangulation;
638 
642  const FE_Q<dim> fe;
643 
647  DoFHandler<dim> dof_handler;
648 
653  Vector<double> solution;
654 
658  Vector<double> projected_exact_solution;
659 
663  AffineConstraints<double> constraints;
664 
668  SparsityPattern sparsity_pattern;
669 
673  SparseMatrix<double> system_matrix;
674 
678  Vector<double> system_rhs;
679 
683  double L2_norm;
684 
688  double Linfty_norm;
689 
693  double H1_norm;
694 
695 private:
696  const unsigned int mapping_degree;
697  const unsigned int type_of_pde_rhs;
698  const std::string fname;
699  const Function<dim>* exact_solution;
700  const bool axisymmetric;
701  const bool vector_potential;
702  const bool print_time_tables;
703  const bool project_exact_solution;
704  const bool write_higher_order_cells;
705 
706  Vector<float> L2_per_cell;
707  Vector<float> Linfty_per_cell;
708  Vector<float> H1_per_cell;
709 
710  // ----------------------------------------------------------------------------
711  // These structures and functions are related to the Work Stream algorithm.
712  // See article "WorkStream – A Design Pattern for Multicore-Enabled Finite
713  // Element Computations." by BRUNO TURCKSIN, MARTIN KRONBICHLER,
714  // WOLFGANG BANGERTH for more details.
715  // ----------------------------------------------------------------------------
716  struct AssemblyScratchData
717  {
718  AssemblyScratchData(const FiniteElement<dim>& fe,
719  unsigned int type_of_pde_rhs,
720  bool axisymmetric,
721  bool vector_potential,
722  unsigned int mapping_degree);
723 
724  AssemblyScratchData(const AssemblyScratchData& scratch_data);
725 
726  TheCoefficient<dim, stage> the_coefficient;
727  PdeRhs<dim, stage> pde_rhs;
728  PdeRhsCvp<dim, stage> pde_rhs_cvp;
729  Gamma<dim, stage> gamma;
730  RobinRhs<dim, stage> robin_rhs;
731  FreeSurfaceCharge<dim, stage> free_surface_charge;
732 
733  MappingQ<dim> mapping;
735  FEValues<dim> fe_values;
736  FEFaceValues<dim> fe_face_values;
737 
738  const unsigned int dofs_per_cell;
739  const unsigned int n_q_points;
740  const unsigned int n_q_points_face;
741 
742  std::vector<double> the_coefficient_list;
743  std::vector<double> pde_rhs_list;
744  std::vector<Tensor<1, dim>> pde_rhs_cvp_list; // Used in 2D only
745  std::vector<Tensor<1, dim>> pde_rhs_cvp_list_face; // Used in 2D only
746  std::vector<double> gamma_list;
747  std::vector<double> robin_rhs_list;
748  std::vector<double> free_surface_charge_list;
749 
750  const unsigned int type_of_pde_rhs;
751  const bool axisymmetric;
752  const bool vector_potential;
753  double axi_mult; // Equals the distance to the axis of rotation symmetry, r,
754  // in the "Recipe for static scalar solver in 2D
755  // (axisymmetric)". Equals 1.0 in all other recipes. All
756  // integrands are multiplied by this multiplier.
757 
758  // Four auxiliary variables.
759  bool do_robin;
760  bool do_kappa;
761  bool do_Jf_on_boundary;
762  double robin_rhs_or_kappa;
763  };
764 
765  struct AssemblyCopyData
766  {
767  FullMatrix<double> cell_matrix;
768  Vector<double> cell_rhs;
769  std::vector<types::global_dof_index> local_dof_indices;
770  };
771 
772  void system_matrix_local(
773  const typename DoFHandler<dim>::active_cell_iterator& cell,
774  AssemblyScratchData& scratch_data,
775  AssemblyCopyData& copy_data);
776 
777  void copy_local_to_global(const AssemblyCopyData& copy_data);
778  //-----------------------------------------------------------------------------
779  //-----------------------------------------------------------------------------
780  //-----------------------------------------------------------------------------
781 };
782 
783 template<int dim, int stage>
784 void
786 {
787  dof_handler.reinit(triangulation);
788  dof_handler.distribute_dofs(fe);
789 
790  constraints.clear();
791  DoFTools::make_hanging_node_constraints(dof_handler, constraints);
792 
793 #pragma GCC diagnostic push
794 #pragma GCC diagnostic ignored "-Wunused-but-set-variable"
795  for (auto item : dirichlet_stack) {
796  Assert(item.first % 2 == 1, ExcInternalError());
797  }
798 #pragma GCC diagnostic pop
799 
800  VectorTools::interpolate_boundary_values(
801  MappingQ<dim>(mapping_degree), dof_handler, dirichlet_stack, constraints);
802 
803  constraints.close();
804 
805  DynamicSparsityPattern dsp(dof_handler.n_dofs(), dof_handler.n_dofs());
806  DoFTools::make_sparsity_pattern(dof_handler, dsp, constraints, false);
807 
808  sparsity_pattern.copy_from(dsp);
809  system_matrix.reinit(sparsity_pattern);
810  solution.reinit(dof_handler.n_dofs());
811  system_rhs.reinit(dof_handler.n_dofs());
812 
813  if (project_exact_solution)
814  projected_exact_solution.reinit(dof_handler.n_dofs());
815 
816  if (exact_solution) {
817  L2_per_cell.reinit(triangulation.n_active_cells());
818  H1_per_cell.reinit(triangulation.n_active_cells());
819  Linfty_per_cell.reinit(triangulation.n_active_cells());
820  }
821 }
822 
823 template<int dim, int stage>
824 void
826 {
827  WorkStream::run(
828  dof_handler.begin_active(),
829  dof_handler.end(),
830  *this,
831  &Solver::system_matrix_local,
832  &Solver::copy_local_to_global,
833  AssemblyScratchData(
834  fe, type_of_pde_rhs, axisymmetric, vector_potential, mapping_degree),
835  AssemblyCopyData());
836 }
837 
838 template<int dim, int stage>
840  const FiniteElement<dim>& fe,
841  unsigned int type_of_pde_rhs,
842  bool axisymmetric,
843  bool vector_potential,
844  unsigned int mapping_degree)
845  : mapping(mapping_degree)
846  , qt(fe.degree)
847  , fe_values(mapping,
848  fe,
849  QGauss<dim>(qt.sim()),
850  update_gradients | update_values | update_quadrature_points |
851  update_JxW_values)
852  , fe_face_values(mapping,
853  fe,
854  QGauss<dim - 1>(qt.sim()),
855  update_values | update_normal_vectors |
856  update_quadrature_points | update_JxW_values)
857  , dofs_per_cell(fe_values.dofs_per_cell)
858  , n_q_points(fe_values.get_quadrature().size())
859  , n_q_points_face(fe_face_values.get_quadrature().size())
860  , the_coefficient_list(n_q_points)
861  , pde_rhs_list(n_q_points)
862  , pde_rhs_cvp_list(n_q_points, Tensor<1, dim>())
863  , pde_rhs_cvp_list_face(n_q_points_face, Tensor<1, dim>())
864  , gamma_list(n_q_points_face)
865  , robin_rhs_list(n_q_points_face)
866  , free_surface_charge_list(n_q_points_face)
867  , type_of_pde_rhs(type_of_pde_rhs)
868  , axisymmetric(axisymmetric)
869  , vector_potential(vector_potential)
870  , axi_mult(1.0)
871 {
872 }
873 
874 template<int dim, int stage>
875 Solver<dim, stage>::AssemblyScratchData::AssemblyScratchData(
876  const AssemblyScratchData& scratch_data)
877  : mapping(scratch_data.mapping.get_degree())
878  , qt(scratch_data.qt)
879  , fe_values(mapping,
880  scratch_data.fe_values.get_fe(),
881  scratch_data.fe_values.get_quadrature(),
882  update_gradients | update_values | update_quadrature_points |
883  update_JxW_values)
884  , fe_face_values(mapping,
885  scratch_data.fe_face_values.get_fe(),
886  scratch_data.fe_face_values.get_quadrature(),
887  update_values | update_normal_vectors |
888  update_quadrature_points | update_JxW_values)
889  , dofs_per_cell(fe_values.dofs_per_cell)
890  , n_q_points(fe_values.get_quadrature().size())
891  , n_q_points_face(fe_face_values.get_quadrature().size())
892  , the_coefficient_list(n_q_points)
893  , pde_rhs_list(n_q_points)
894  , pde_rhs_cvp_list(n_q_points, Tensor<1, dim>())
895  , pde_rhs_cvp_list_face(n_q_points_face, Tensor<1, dim>())
896  , gamma_list(n_q_points_face)
897  , robin_rhs_list(n_q_points_face)
898  , free_surface_charge_list(n_q_points_face)
899  , type_of_pde_rhs(scratch_data.type_of_pde_rhs)
900  , axisymmetric(scratch_data.axisymmetric)
901  , vector_potential(scratch_data.vector_potential)
902  , axi_mult(1.0)
903 {
904 }
905 
906 template<int dim, int stage>
907 void
908 Solver<dim, stage>::system_matrix_local(
909  const typename DoFHandler<dim>::active_cell_iterator& cell,
910  AssemblyScratchData& scratch_data,
911  AssemblyCopyData& copy_data)
912 {
913  // See the following boxes:
914  // (1) Recipe for static scalar solver in 3D
915  // (2) Recipe for static scalar solver in 2D (planar)
916  // (3) Recipe for static scalar solver in 2D (axisymmetric)
917  // (4) Recipe for static scalar solver in 2D (current vect. potential)
918 
919  // The comments below refer to these recipes by number, i.e., recipe (1),
920  // recipe (2), recipe (3), and recipe (4).
921 
922  copy_data.cell_matrix.reinit(scratch_data.dofs_per_cell,
923  scratch_data.dofs_per_cell);
924 
925  copy_data.cell_rhs.reinit(scratch_data.dofs_per_cell);
926 
927  copy_data.local_dof_indices.resize(scratch_data.dofs_per_cell);
928 
929  scratch_data.fe_values.reinit(cell);
930 
931  if ((scratch_data.type_of_pde_rhs == 2) ||
932  (scratch_data.type_of_pde_rhs == 3)) {
933  // The coefficient equals 1.0 if the current vector potential, T, is
934  // computed.
935  for (unsigned int q_index = 0; q_index < scratch_data.n_q_points; ++q_index)
936  scratch_data.the_coefficient_list[q_index] = 1.0;
937  } else {
938  scratch_data.the_coefficient.value_list(
939  scratch_data.fe_values.get_quadrature_points(),
940  cell->material_id(),
941  cell->user_index(),
942  scratch_data.the_coefficient_list);
943  }
944 
945  if (scratch_data.type_of_pde_rhs == 1)
946  scratch_data.pde_rhs.value_list(
947  scratch_data.fe_values.get_quadrature_points(),
948  cell->material_id(),
949  cell->user_index(),
950  scratch_data.pde_rhs_list);
951 
952  if ((scratch_data.type_of_pde_rhs == 2) ||
953  (scratch_data.type_of_pde_rhs == 3))
954  scratch_data.pde_rhs_cvp.value_list(
955  scratch_data.fe_values.get_quadrature_points(),
956  cell->material_id(),
957  cell->user_index(),
958  scratch_data.pde_rhs_cvp_list);
959 
960  for (unsigned int q_index = 0; q_index < scratch_data.n_q_points; ++q_index) {
961  if ((scratch_data.axisymmetric) && (!scratch_data.vector_potential)) {
962  scratch_data.axi_mult =
963  scratch_data.fe_values.quadrature_point(q_index)[0];
964  }
965 
966  for (unsigned int i = 0; i < scratch_data.dofs_per_cell; ++i) {
967  for (unsigned int j = 0; j < scratch_data.dofs_per_cell; ++j) {
968  // Integral I_a1 in recipes (1), (2), (3), and (4).
969  copy_data.cell_matrix(i, j) +=
970  scratch_data.axi_mult *
971  scratch_data.the_coefficient_list[q_index] * // epsilon
972  scratch_data.fe_values.shape_grad(i, q_index) * // grad N_i
973  scratch_data.fe_values.shape_grad(j, q_index) * // grad N_j
974  scratch_data.fe_values.JxW(q_index); // dV
975  }
976 
977  switch (scratch_data.type_of_pde_rhs) {
978  case 0:
979  // Integral I_b3 in recipes (1), (2), and (3) with rho_f=0.
980  copy_data.cell_rhs(i) = 0.0;
981  break;
982  case 1:
983  // Integral I_b3 in recipes (1), (2), and (3).
984  copy_data.cell_rhs(i) +=
985  scratch_data.axi_mult * scratch_data.pde_rhs_list[q_index] * // rho
986  scratch_data.fe_values.shape_value(i, q_index) * // N_i
987  scratch_data.fe_values.JxW(q_index); // dV
988  break;
989  case 2:
990  case 3:
991  // Integral I_b3-1 in recipe (4).
992  copy_data.cell_rhs(i) +=
993  (scratch_data.pde_rhs_cvp_list[q_index][0] *
994  scratch_data.fe_values.shape_grad(i, q_index)[1] -
995  scratch_data.pde_rhs_cvp_list[q_index][1] *
996  scratch_data.fe_values.shape_grad(i, q_index)[0]) *
997  scratch_data.fe_values.JxW(q_index); // (Jf . curl_v N_i) dS
998  break;
999  default:
1000  Assert(false,
1001  ExcMessage(
1002  "The parameter type_of_pde_rhs equals " +
1003  std::to_string(scratch_data.type_of_pde_rhs) +
1004  ". Only the following values are allowed: 0, 1, and 2."));
1005  break;
1006  }
1007  }
1008  }
1009 
1010  for (unsigned int f = 0; f < GeometryInfo<dim>::faces_per_cell; ++f) {
1011  scratch_data.do_robin = (cell->face(f)->at_boundary() &&
1012  (cell->face(f)->boundary_id() % 2 == 0) &&
1013  (cell->face(f)->boundary_id() != 0));
1014 
1015  scratch_data.do_kappa =
1016  ((cell->user_index() > 0) && (cell->face(f)->user_index() > 0) &&
1017  (scratch_data.type_of_pde_rhs < 2));
1018 
1019  scratch_data.do_Jf_on_boundary =
1020  (cell->face(f)->at_boundary() && (scratch_data.type_of_pde_rhs == 3));
1021 
1022  Assert(
1023  !(scratch_data.do_robin && scratch_data.do_kappa),
1024  ExcMessage(
1025  "Robin boundary condition is applied on a boundary. The surface free-current \
1026 charge, kappa_f, exists only on interfaces. No interface is a boundary. \
1027 Therefore, do_robin and do_kappa are mutually exclusive."));
1028 
1029  Assert(
1030  !(scratch_data.do_kappa && scratch_data.do_Jf_on_boundary),
1031  ExcMessage(
1032  "When computing the current vector potential, type_of_pde = 2, J_f is \
1033 integrated over the boundary. The surface free-current charge, kappa_f \
1034 exists only on interfaces. No interface is a boundary. Therefore, \
1035 do_Jf_on_boundary and do_kappa are mutually exclusive."));
1036 
1037  if (scratch_data.do_robin || scratch_data.do_kappa ||
1038  scratch_data.do_Jf_on_boundary) {
1039  scratch_data.fe_face_values.reinit(cell, f);
1040 
1041  if (scratch_data.do_robin) {
1042  scratch_data.gamma.value_list(
1043  scratch_data.fe_face_values.get_quadrature_points(),
1044  scratch_data.fe_face_values.get_normal_vectors(),
1045  cell->face(f)->boundary_id(),
1046  cell->material_id(),
1047  cell->user_index(),
1048  cell->face(f)->user_index(),
1049  scratch_data.gamma_list);
1050 
1051  scratch_data.robin_rhs.value_list(
1052  scratch_data.fe_face_values.get_quadrature_points(),
1053  scratch_data.fe_face_values.get_normal_vectors(),
1054  cell->face(f)->boundary_id(),
1055  cell->material_id(),
1056  cell->user_index(),
1057  cell->face(f)->user_index(),
1058  scratch_data.robin_rhs_list);
1059  }
1060 
1061  if (scratch_data.do_kappa)
1062  scratch_data.free_surface_charge.value_list(
1063  scratch_data.fe_face_values.get_quadrature_points(),
1064  scratch_data.fe_face_values.get_normal_vectors(),
1065  cell->material_id(),
1066  cell->user_index(),
1067  cell->face(f)->user_index(),
1068  scratch_data.free_surface_charge_list);
1069 
1070  if (scratch_data.do_Jf_on_boundary)
1071  scratch_data.pde_rhs_cvp.value_list(
1072  scratch_data.fe_face_values.get_quadrature_points(),
1073  cell->material_id(),
1074  cell->user_index(),
1075  scratch_data.pde_rhs_cvp_list_face);
1076 
1077  for (unsigned int q_index_face = 0;
1078  q_index_face < scratch_data.n_q_points_face;
1079  ++q_index_face) {
1080  if ((scratch_data.axisymmetric) && (!scratch_data.vector_potential))
1081  scratch_data.axi_mult =
1082  scratch_data.fe_face_values.quadrature_point(q_index_face)[0];
1083 
1084  for (unsigned int i = 0; i < scratch_data.dofs_per_cell; ++i) {
1085  if (scratch_data.do_robin) {
1086  for (unsigned int j = 0; j < scratch_data.dofs_per_cell; ++j) {
1087  // Integral I_a2 in recipes (1), (2), and (3).
1088  copy_data.cell_matrix(i, j) +=
1089  scratch_data.axi_mult *
1090  scratch_data.gamma_list[q_index_face] * // gamma
1091  scratch_data.fe_face_values.shape_value(i,
1092  q_index_face) * // N_i
1093  scratch_data.fe_face_values.shape_value(j,
1094  q_index_face) * // N_j
1095  scratch_data.fe_face_values.JxW(q_index_face); // dS
1096  }
1097  }
1098 
1099  if (scratch_data.do_robin || scratch_data.do_kappa) {
1100  scratch_data.robin_rhs_or_kappa = 0.0;
1101 
1102  // If true, the integral below is I_b1.
1103  if (scratch_data.do_robin)
1104  scratch_data.robin_rhs_or_kappa =
1105  scratch_data.robin_rhs_list[q_index_face];
1106 
1107  // If true, the integral below is I_b2.
1108  if (scratch_data.do_kappa)
1109  scratch_data.robin_rhs_or_kappa =
1110  scratch_data.free_surface_charge_list[q_index_face];
1111 
1112  // Depending on the current context:
1113  // integral I_b1 in recipes (1), (2), (3), and (4)
1114  // or
1115  // integral I_b2 in recipes (1), (2), and (3).
1116  copy_data.cell_rhs(i) +=
1117  scratch_data.axi_mult *
1118  scratch_data.robin_rhs_or_kappa * // sigma or kappa_f
1119  scratch_data.fe_face_values.shape_value(i, q_index_face) * // N_i
1120  scratch_data.fe_face_values.JxW(q_index_face); // dS
1121  }
1122 
1123  if (scratch_data.do_Jf_on_boundary) {
1124  // Integral I_b3-2 in recipe (4).
1125  copy_data.cell_rhs(i) -=
1126  scratch_data.fe_face_values.shape_value(i, q_index_face) *
1127  (scratch_data.pde_rhs_cvp_list_face[q_index_face][0] *
1128  scratch_data.fe_face_values.normal_vector(q_index_face)[1] -
1129  scratch_data.pde_rhs_cvp_list_face[q_index_face][1] *
1130  scratch_data.fe_face_values.normal_vector(
1131  q_index_face)[0] // V
1132  ) *
1133  scratch_data.fe_face_values.JxW(
1134  q_index_face); // J_f.(n x N_i)dl =
1135  // S
1136  // = N_i(J_f x n)dl
1137  }
1138  } // for (unsigned int i = 0; ...
1139  } // for (unsigned int q_index_face = 0; ...
1140  } // if (scratch_data.do_robin || scratch_data.do_kappa ||
1141  // scratch_data.do_Jf_on_boundary)
1142  } // for (unsigned int f = 0; ...
1143 
1144  cell->get_dof_indices(copy_data.local_dof_indices);
1145 }
1146 
1147 template<int dim, int stage>
1148 void
1149 Solver<dim, stage>::copy_local_to_global(const AssemblyCopyData& copy_data)
1150 {
1151  constraints.distribute_local_to_global(copy_data.cell_matrix,
1152  copy_data.cell_rhs,
1153  copy_data.local_dof_indices,
1154  system_matrix,
1155  system_rhs);
1156 }
1157 
1158 template<int dim, int stage>
1159 void
1160 compute_L2_error_norm(const DoFHandler<dim>& dof_handler,
1161  const Triangulation<dim>& triangulation,
1162  const Vector<double>& solution,
1163  const Function<dim>* exact_solution,
1164  Vector<float>& L2_per_cell,
1165  double& L2_norm,
1166  unsigned int mapping_degree)
1167 {
1168  Weight<dim, stage> weight;
1169  const Function<dim, double>* mask = &weight;
1170 
1171  Constants::QuadratureTableScalar<dim> qt(dof_handler.get_fe().degree);
1172  QGauss<dim> quadrature(qt.enorm());
1173 
1174  VectorTools::integrate_difference(MappingQ<dim>(mapping_degree),
1175  dof_handler,
1176  solution,
1177  *exact_solution,
1178  L2_per_cell,
1179  quadrature,
1180  VectorTools::L2_norm,
1181  mask);
1182 
1183  L2_norm = VectorTools::compute_global_error(
1184  triangulation, L2_per_cell, VectorTools::L2_norm);
1185 }
1186 
1187 template<int dim, int stage>
1188 void
1189 compute_H1_error_norm(const DoFHandler<dim>& dof_handler,
1190  const Triangulation<dim>& triangulation,
1191  const Vector<double>& solution,
1192  const Function<dim>* exact_solution,
1193  Vector<float>& H1_per_cell,
1194  double& H1_norm,
1195  unsigned int mapping_degree)
1196 {
1197  Weight<dim, stage> weight;
1198  const Function<dim, double>* mask = &weight;
1199 
1200  Constants::QuadratureTableScalar<dim> qt(dof_handler.get_fe().degree);
1201  QGauss<dim> quadrature(qt.enorm());
1202 
1203  VectorTools::integrate_difference(MappingQ<dim>(mapping_degree),
1204  dof_handler,
1205  solution,
1206  *exact_solution,
1207  H1_per_cell,
1208  quadrature,
1209  VectorTools::H1_seminorm,
1210  mask);
1211 
1212  H1_norm = VectorTools::compute_global_error(
1213  triangulation, H1_per_cell, VectorTools::H1_seminorm);
1214 }
1215 
1216 template<int dim, int stage>
1217 void
1218 compute_Linfty_error_norm(const DoFHandler<dim>& dof_handler,
1219  const Triangulation<dim>& triangulation,
1220  const Vector<double>& solution,
1221  const Function<dim>* exact_solution,
1222  Vector<float>& Linfty_per_cell,
1223  double& Linfty_norm,
1224  unsigned int mapping_degree)
1225 {
1226  Weight<dim, stage> weight;
1227  const Function<dim, double>* mask = &weight;
1228 
1229  Constants::QuadratureTableScalar<dim> qt(dof_handler.get_fe().degree);
1230  QGauss<dim> quadrature(qt.enorm());
1231 
1232  VectorTools::integrate_difference(MappingQ<dim>(mapping_degree),
1233  dof_handler,
1234  solution,
1235  *exact_solution,
1236  Linfty_per_cell,
1237  QGauss<dim>(1),
1238  VectorTools::Linfty_norm,
1239  mask);
1240 
1241  Linfty_norm = VectorTools::compute_global_error(
1242  triangulation, Linfty_per_cell, VectorTools::Linfty_norm);
1243 }
1244 
1245 template<int dim, int stage>
1246 void
1248 {
1249  if (exact_solution) {
1250  Threads::Task<void> task_l2 =
1251  Threads::new_task(&compute_L2_error_norm<dim, stage>,
1252  dof_handler,
1253  triangulation,
1254  solution,
1255  exact_solution,
1256  L2_per_cell,
1257  L2_norm,
1258  mapping_degree);
1259 
1260  Threads::Task<void> task_h1 =
1261  Threads::new_task(&compute_H1_error_norm<dim, stage>,
1262  dof_handler,
1263  triangulation,
1264  solution,
1265  exact_solution,
1266  H1_per_cell,
1267  H1_norm,
1268  mapping_degree);
1269 
1270  Threads::Task<void> task_linfty =
1271  Threads::new_task(&compute_Linfty_error_norm<dim, stage>,
1272  dof_handler,
1273  triangulation,
1274  solution,
1275  exact_solution,
1276  Linfty_per_cell,
1277  Linfty_norm,
1278  mapping_degree);
1279  }
1280 }
1281 
1282 template<int dim, int stage>
1283 void
1285 {
1287 
1288  AffineConstraints<double> constraints_empty;
1289  constraints_empty.close();
1290 
1291  VectorTools::project(MappingQ<dim>(mapping_degree),
1292  dof_handler,
1293  constraints_empty,
1294  QGauss<dim>(qt.sim()),
1295  *exact_solution,
1296  projected_exact_solution);
1297 }
1298 
1299 template<int dim, int stage>
1300 void
1302 {
1303  DataOut<dim> data_out;
1304 
1305  data_out.attach_dof_handler(dof_handler);
1306  data_out.add_data_vector(solution, "ScalarField");
1307 
1308  if (exact_solution) {
1309  data_out.add_data_vector(L2_per_cell, "L2norm");
1310  data_out.add_data_vector(Linfty_per_cell, "LinftyNorm");
1311  data_out.add_data_vector(H1_per_cell, "H1seminorm");
1312 
1313  if (project_exact_solution)
1314  data_out.add_data_vector(projected_exact_solution, "ScalarFieldExact");
1315  }
1316 
1317  std::ofstream ofs;
1318 
1319  if (write_higher_order_cells) {
1320  DataOutBase::VtkFlags flags;
1321  flags.write_higher_order_cells = true;
1322  data_out.set_flags(flags);
1323 
1324  const MappingQ<dim> mapping(mapping_degree);
1325 
1326  data_out.build_patches(mapping,
1327  fe.degree + 2,
1328  DataOut<dim>::CurvedCellRegion::curved_inner_cells);
1329 
1330  ofs.open(fname + ".vtu");
1331  data_out.write_vtu(ofs);
1332 
1333  } else {
1334 
1335  data_out.build_patches();
1336 
1337  ofs.open(fname + ".vtk");
1338  data_out.write_vtk(ofs);
1339  }
1340 
1341  ofs.close();
1342 }
1343 
1344 template<int dim, int stage>
1345 void
1347 {
1348  std::ofstream ofs_matrix(fname + "_matrix.csv");
1349  std::ofstream ofs_rhs(fname + "_rhs.csv");
1350 
1351  for (unsigned int i = 0; i < system_matrix.m(); ++i) {
1352  ofs_rhs << system_rhs(i);
1353  if (i < (system_matrix.m() - 1))
1354  ofs_rhs << "\n";
1355 
1356  for (unsigned int j = 0; j < system_matrix.n(); ++j) {
1357  ofs_matrix << std::scientific << std::setprecision(16)
1358  << system_matrix.el(i, j);
1359 
1360  if (j < (system_matrix.m() - 1))
1361  ofs_matrix << ", ";
1362  }
1363  if (i < (system_matrix.m() - 1))
1364  ofs_matrix << "\n";
1365  }
1366 
1367  ofs_rhs.close();
1368  ofs_matrix.close();
1369 }
1370 
1371 } // namespace StaticScalarSolver
1372 
1373 #endif
The tables that contain the amount of quadrature points used in the scalar problems.
Definition: constants.hpp:61
unsigned int sim() const
Returns the amount of quadrature points used when assembling system if linear equations.
Solves static scalar boundary value problem.
std::map< types::boundary_id, const Function< dim > * > dirichlet_stack
A map that contains pairs of boundary IDs and the corresponding Dirichlet boundary conditions.
double H1_norm
The error semi-norm.
double Linfty_norm
The error norm.
void save() const
Saves simulation results into a vtk or vtu file.
void assemble()
Assembles the system matrix and the right-hand side vector.
Triangulation< dim > triangulation
The mesh.
const Triangulation< dim > & get_tria() const
Returns a reference to triangulation.
const Vector< double > & get_solution() const
Returns a reference to the solution.
DoFHandler< dim > dof_handler
The degrees-of-freedom handler.
SparsityPattern sparsity_pattern
The sparsity pattern of the system matrix.
double get_L2_norm() const
Returns error norm.
void clear()
Releases computer memory associated with the system matrix and right-hand side.
const DoFHandler< dim > & get_dof_handler() const
Returns a reference to dof handler.
void project_exact_solution_fcn()
Projects exact solution.
double get_H1_norm() const
Returns error norm.
Vector< double > system_rhs
The system right-hand side vector.
unsigned int get_n_dofs() const
Returns the total amount of the degrees of freedom.
Vector< double > solution
The solution vector, i.e., degrees of freedom yielded by the simulation.
double get_Linfty_norm() const
Returns error norm.
Vector< double > projected_exact_solution
The projected exact solution vector.
AffineConstraints< double > constraints
The constraints associated with the Dirichlet boundary conditions.
unsigned int get_n_cells() const
Returns the number of active cells in the mesh.
SparseMatrix< double > system_matrix
The system matrix.
void compute_error_norms()
Computes error norms.
unsigned int get_type_of_pde_rhs() const
Returns the value of type_of_pde_rhs.
virtual void solve()=0
Solves the system of linear equations.
unsigned int get_n_used_vertices() const
Returns the number of used vertices.
const FE_Q< dim > fe
The finite elements.
void save_matrix_and_rhs_to_csv(std::string fname) const
Saves the system matrix and the right-hand side into a csv file.
virtual void fill_dirichlet_stack()=0
Initializes the data member StaticScalarSolver::Solver::dirichlet_stack.
void setup()
Initializes system matrix and the right-hand side vector, etc.
unsigned int get_mapping_degree() const
Returns degree of the interpolating Lagrange polynomials used for mapping from the reference cell to ...
Solver(unsigned int p, unsigned int mapping_degree, unsigned int type_of_pde_rhs, std::string fname="data", const Function< dim > *exact_solution=nullptr, bool axisymmetric=false, bool vector_potential=false, bool print_time_tables=false, bool project_exact_solution=false, bool write_higher_order_cells=false)
The only constructor.
void run()
Runs the simulation.
virtual void make_mesh()=0
Initializes the data member StaticScalarSolver::Solver::triangulation.
unsigned int get_n_vertices() const
Returns the number of vertices.
unsigned int get_n_lines() const
Returns the number of lines.