Logbook  (07-04-2025)
Static problems
static_vector_solver_i.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 StaticVectorSolverI_H__
13 #define StaticVectorSolverI_H__
14 
15 #include <deal.II/base/exceptions.h>
16 #include <deal.II/base/tensor.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_renumbering.h>
26 #include <deal.II/dofs/dof_tools.h>
27 
28 #include <deal.II/fe/fe_nedelec.h>
29 #include <deal.II/fe/fe_values.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 "settings.hpp"
52 #include "static_vector_input.hpp"
53 
54 #define VE scratch_data.ve
55 
56 #define TMR(__name) TimerOutput::Scope timer_section(timer, __name)
57 
58 using namespace dealii;
59 
60 namespace StaticVectorSolver {
317 template<int dim, int stage = 1>
318 class Solver1
319 {
320 public:
321  Solver1() = delete;
322 
352  Solver1(unsigned int p,
353  unsigned int mapping_degree,
354  unsigned int type_of_pde_rhs = 3,
355  double eta_squared = 0.0,
356  std::string fname = "data",
357  const Function<dim>* exact_solution = nullptr,
358  bool print_time_tables = false,
359  bool project_exact_solution = false,
360  bool write_higher_order_cells = false)
361  : fe(p)
362  , mapping_degree(mapping_degree)
363  , type_of_pde_rhs(type_of_pde_rhs)
364  , eta_squared(eta_squared)
365  , fname(fname)
366  , exact_solution(exact_solution)
367  , print_time_tables(print_time_tables)
368  , project_exact_solution(project_exact_solution)
369  , write_higher_order_cells(write_higher_order_cells)
370  {
371  Assert(((dim == 2) || (dim == 3)), ExcInternalError());
372  Assert(p < 5, ExcInternalError());
373  Assert(type_of_pde_rhs < 4, ExcInternalError());
374  }
375 
385  virtual void make_mesh() = 0;
386 
416  virtual void fill_dirichlet_stack() = 0;
417 
421  virtual void solve() = 0;
422 
432  void setup();
433 
437  void assemble();
438 
443 
455 
488  void save() const;
489 
505  void save_matrix_and_rhs_to_csv(std::string fname) const;
506 
511  void clear()
512  {
513  system_matrix.clear();
514  system_rhs.reinit(0);
515  }
516 
520  const Triangulation<dim>& get_tria() const { return triangulation; }
521 
525  const DoFHandler<dim>& get_dof_handler() const { return dof_handler; }
526 
530  const Vector<double>& get_solution() const { return solution; }
531 
535  unsigned int get_n_cells() const
536  {
537  return static_cast<unsigned int>(triangulation.n_active_cells());
538  }
539 
543  unsigned int get_n_dofs() const
544  {
545  return static_cast<unsigned int>(dof_handler.n_dofs());
546  }
547 
551  unsigned int get_rhs_type() const { return type_of_pde_rhs; }
552 
556  double get_L2_norm() const { return L2_norm; }
557 
561  double get_Linfty_norm() const { return Linfty_norm; }
562 
567  unsigned int get_mapping_degree() const { return mapping_degree; }
568 
576  void run()
577  {
578  TimerOutput::OutputFrequency tf =
579  (print_time_tables) ? TimerOutput::summary : TimerOutput::never;
580 
581  TimerOutput timer(std::cout, tf, TimerOutput::cpu_and_wall_times_grouped);
582 
583  {
584  TMR("Make mesh");
585  make_mesh();
586  }
587  {
588  TMR("Fill Dirichlet stack");
589  fill_dirichlet_stack();
590  }
591  {
592  TMR("Setup");
593  setup();
594  }
595  {
596  TMR("Assemble");
597  assemble();
598  }
599  {
600  TMR("Solve");
601  solve();
602  }
603 
604  if (exact_solution) {
605  if (project_exact_solution) {
606  TMR("Project exact solution");
607  project_exact_solution_fcn();
608  }
609 
610  {
611  TMR("Compute error norms");
612  compute_error_norms();
613  }
614  }
615 
616  {
617  TMR("Save");
618  save();
619  }
620  };
621 
622  virtual ~Solver1() = default;
623 
624 protected:
629  std::map<types::boundary_id, const Function<dim>*> dirichlet_stack;
630 
634  Triangulation<dim> triangulation;
635 
639  const FE_Nedelec<dim> fe;
640 
644  DoFHandler<dim> dof_handler;
645 
650  Vector<double> solution;
651 
655  Vector<double> projected_exact_solution;
656 
660  AffineConstraints<double> constraints;
661 
665  SparsityPattern sparsity_pattern;
666 
670  SparseMatrix<double> system_matrix;
671 
675  Vector<double> system_rhs;
676 
680  double L2_norm;
681 
685  double Linfty_norm;
686 
687 private:
688  const unsigned int mapping_degree;
689  const unsigned int type_of_pde_rhs;
690  const double eta_squared;
691  const std::string fname;
692  const Function<dim>* exact_solution;
693  const bool print_time_tables;
694  const bool project_exact_solution;
695  const bool write_higher_order_cells;
696 
697  Vector<float> L2_per_cell;
698  Vector<float> Linfty_per_cell;
699 
700  // ----------------------------------------------------------------------------
701  // These structures and functions are related to the Work Stream algorithm.
702  // See article "WorkStream – A Design Pattern for Multicore-Enabled Finite
703  // Element Computations." by BRUNO TURCKSIN, MARTIN KRONBICHLER,
704  // WOLFGANG BANGERTH for more details.
705  // ----------------------------------------------------------------------------
706  struct AssemblyScratchData
707  {
708  AssemblyScratchData(const FiniteElement<dim>& fe,
709  unsigned int mapping_degree,
710  unsigned int type_of_pde_rhs,
711  double eta_squared);
712 
713  AssemblyScratchData(const AssemblyScratchData& scratch_data);
714 
715  TheCoefficient<dim, stage> the_coefficient;
716  PdeRhs<dim, stage> pde_rhs;
717  Gamma<dim, stage> gamma;
718  RobinRhs<dim, stage> robin_rhs;
719  FreeSurfaceCurrent<dim, stage> free_surface_current;
720 
721  MappingQ<dim> mapping;
723  FEValues<dim> fe_values;
724  FEFaceValues<dim> fe_face_values;
725 
726  const unsigned int dofs_per_cell;
727  const unsigned int n_q_points;
728  const unsigned int n_q_points_face;
729 
730  std::vector<double> the_coefficient_list;
731  std::vector<Tensor<1, dim>> pde_rhs_list;
732  std::vector<Tensor<1, dim>> pde_rhs_list_face;
733  std::vector<double> gamma_list;
734  std::vector<Tensor<1, dim>> robin_rhs_list;
735  std::vector<Tensor<1, dim>> free_surface_current_list;
736 
737  const unsigned int type_of_pde_rhs;
738  const double eta_squared;
739  const FEValuesExtractors::Vector ve;
740  bool do_robin;
741  bool do_K;
742  bool do_T_on_boundary;
743  };
744 
745  struct AssemblyCopyData
746  {
747  FullMatrix<double> cell_matrix;
748  Vector<double> cell_rhs;
749  std::vector<types::global_dof_index> local_dof_indices;
750  };
751 
752  void system_matrix_local(
753  const typename DoFHandler<dim>::active_cell_iterator& cell,
754  AssemblyScratchData& scratch_data,
755  AssemblyCopyData& copy_data);
756 
757  void copy_local_to_global(const AssemblyCopyData& copy_data);
758  //-----------------------------------------------------------------------------
759  //-----------------------------------------------------------------------------
760  //-----------------------------------------------------------------------------
761 };
762 
763 template<int dim, int stage>
764 void
766 {
767  dof_handler.reinit(triangulation);
768  dof_handler.distribute_dofs(fe);
769 
770  constraints.clear();
771  DoFTools::make_hanging_node_constraints(dof_handler, constraints);
772 
773 #pragma GCC diagnostic push
774 #pragma GCC diagnostic ignored "-Wunused-but-set-variable"
775  for (auto item : dirichlet_stack) {
776  Assert(item.first % 2 == 1, ExcInternalError());
777  }
778 #pragma GCC diagnostic pop
779 
780  for (auto item : dirichlet_stack)
781  VectorTools::project_boundary_values_curl_conforming_l2(
782  dof_handler,
783  0, // first vector component
784  *item.second, // boundary function
785  item.first, // boundary id
786  constraints, // constraints
787  MappingQ<dim>(mapping_degree));
788 
789  constraints.close();
790 
791  DynamicSparsityPattern dsp(dof_handler.n_dofs(), dof_handler.n_dofs());
792  DoFTools::make_sparsity_pattern(dof_handler, dsp, constraints, false);
793 
794  sparsity_pattern.copy_from(dsp);
795  system_matrix.reinit(sparsity_pattern);
796  solution.reinit(dof_handler.n_dofs());
797  system_rhs.reinit(dof_handler.n_dofs());
798 
799  if (project_exact_solution)
800  projected_exact_solution.reinit(dof_handler.n_dofs());
801 
802  if (exact_solution) {
803  L2_per_cell.reinit(triangulation.n_active_cells());
804  Linfty_per_cell.reinit(triangulation.n_active_cells());
805  }
806 }
807 
808 template<int dim, int stage>
809 void
811 {
812  WorkStream::run(
813  dof_handler.begin_active(),
814  dof_handler.end(),
815  *this,
816  &Solver1::system_matrix_local,
817  &Solver1::copy_local_to_global,
818  AssemblyScratchData(fe, mapping_degree, type_of_pde_rhs, eta_squared),
819  AssemblyCopyData());
820 }
821 
822 template<int dim, int stage>
824  const FiniteElement<dim>& fe,
825  unsigned int mapping_degree,
826  unsigned int type_of_pde_rhs,
827  double eta_squared)
828  : mapping(mapping_degree)
829  , qt(fe.degree - 1)
830  , fe_values(mapping,
831  fe,
832  QGauss<dim>(qt.sim()),
833  update_gradients | update_values | update_quadrature_points |
834  update_JxW_values)
835  , fe_face_values(mapping,
836  fe,
837  QGauss<dim - 1>(qt.sim()),
838  update_values | update_normal_vectors |
839  update_quadrature_points | update_JxW_values)
840  , dofs_per_cell(fe_values.dofs_per_cell)
841  , n_q_points(fe_values.get_quadrature().size())
842  , n_q_points_face(fe_face_values.get_quadrature().size())
843  , the_coefficient_list(n_q_points)
844  , pde_rhs_list(n_q_points, Tensor<1, dim>())
845  , pde_rhs_list_face(n_q_points_face, Tensor<1, dim>())
846  , gamma_list(n_q_points_face)
847  , robin_rhs_list(n_q_points_face, Tensor<1, dim>())
848  , free_surface_current_list(n_q_points_face, Tensor<1, dim>())
849  , type_of_pde_rhs(type_of_pde_rhs)
850  , eta_squared(eta_squared)
851  , ve(0)
852 {
853 }
854 
855 template<int dim, int stage>
856 Solver1<dim, stage>::AssemblyScratchData::AssemblyScratchData(
857  const AssemblyScratchData& scratch_data)
858  : mapping(scratch_data.mapping.get_degree())
859  , qt(scratch_data.qt)
860  , fe_values(mapping,
861  scratch_data.fe_values.get_fe(),
862  scratch_data.fe_values.get_quadrature(),
863  update_gradients | update_values | update_quadrature_points |
864  update_JxW_values)
865  , fe_face_values(mapping,
866  scratch_data.fe_face_values.get_fe(),
867  scratch_data.fe_face_values.get_quadrature(),
868  update_values | update_normal_vectors |
869  update_quadrature_points | update_JxW_values)
870  , dofs_per_cell(fe_values.dofs_per_cell)
871  , n_q_points(fe_values.get_quadrature().size())
872  , n_q_points_face(fe_face_values.get_quadrature().size())
873  , the_coefficient_list(n_q_points)
874  , pde_rhs_list(n_q_points, Tensor<1, dim>())
875  , pde_rhs_list_face(n_q_points_face, Tensor<1, dim>())
876  , gamma_list(n_q_points_face)
877  , robin_rhs_list(n_q_points_face, Tensor<1, dim>())
878  , free_surface_current_list(n_q_points_face, Tensor<1, dim>())
879  , type_of_pde_rhs(scratch_data.type_of_pde_rhs)
880  , eta_squared(scratch_data.eta_squared)
881  , ve(0)
882 {
883 }
884 
885 template<int dim, int stage>
886 void
887 Solver1<dim, stage>::system_matrix_local(
888  const typename DoFHandler<dim>::active_cell_iterator& cell,
889  AssemblyScratchData& scratch_data,
890  AssemblyCopyData& copy_data)
891 {
892  // See the following boxes:
893  // (1) Recipe for static vector solver in 3D
894  // (2) Recipe for static vector solver in 2D
895  // (3) Recipe for static vector solver in 3D (current vect. potential)
896 
897  // The comments below refer to these recipes by number, i.e., recipe (1),
898  // recipe (2), and recipe (3).
899 
900  copy_data.cell_matrix.reinit(scratch_data.dofs_per_cell,
901  scratch_data.dofs_per_cell);
902 
903  copy_data.cell_rhs.reinit(scratch_data.dofs_per_cell);
904 
905  copy_data.local_dof_indices.resize(scratch_data.dofs_per_cell);
906 
907  scratch_data.fe_values.reinit(cell);
908 
909  scratch_data.the_coefficient.value_list(
910  scratch_data.fe_values.get_quadrature_points(),
911  cell->material_id(),
912  cell->user_index(),
913  scratch_data.the_coefficient_list);
914 
915  scratch_data.pde_rhs.value_list(
916  scratch_data.fe_values.get_quadrature_points(),
917  cell->material_id(),
918  cell->user_index(),
919  scratch_data.pde_rhs_list);
920 
921  // The curl of a Nedelec shape function is Tensor<1,3> in three dimensions and
922  // Tensor<1,1> in two dimensions. It is somewhat difficult to fit this into
923  // the "dim" class template paradigm of deal.II. Consequently, we have to use
924  // the if (dim==2) and if (dim==3) filters or instantiate this function
925  // template explicitly for two and three dimensions. We choose the former.
926  //
927  // If the current vector potential, T, is used in recipes (1) and (2), it must
928  // be implemented by the same class that implements the right-hand side of the
929  // curl-curl equation. Neglect the fact that the right-hand side is the curl
930  // of the current vector potential, not the current vector potential itself.
931  // So, "pde_rhs" is a misnomer if the current vector potential is used on the
932  // right-hand side of the PDE. The current vector potential must be
933  // Tensor<1,3> in three dimensions. In two dimensions it must fill the first
934  // component of Tensor<1,2>. The second component is ignored:
935  //
936  // Tensor<1,2> a;
937  // a[0] = T;
938  // a[1] = whatever;
939  //
940  // The same holds for the free-current density, J_f, if the right-hand side of
941  // the PDE is the curl of J_f, i.e., recipe (3).
942 
943  for (unsigned int q_index = 0; q_index < scratch_data.n_q_points; ++q_index) {
944  for (unsigned int i = 0; i < scratch_data.dofs_per_cell; ++i) {
945  for (unsigned int j = 0; j < scratch_data.dofs_per_cell; ++j) {
946  copy_data.cell_matrix(i, j) +=
947  ( // Integral I_a1+I_a3 in recipes (1), (2), and (3).
948  (1.0 / scratch_data.the_coefficient_list[q_index]) * // 1 / mu
949  scratch_data.fe_values[VE].curl(i, q_index) * // curl N_i
950  scratch_data.fe_values[VE].curl(j, q_index) // curl N_j
951  + scratch_data.eta_squared * // eta^2
952  scratch_data.fe_values[VE].value(i, q_index) * // N_i
953  scratch_data.fe_values[VE].value(j, q_index) // N_j
954  ) *
955  scratch_data.fe_values.JxW(q_index); // dV (dS in 2D)
956  }
957 
958  switch (scratch_data.type_of_pde_rhs) {
959  case 0:
960  // Integral I_b3 in recipes (1) and (2) with J_f=0.
961  copy_data.cell_rhs(i) = 0.0;
962  break;
963  case 1:
964  // Integral I_b3 in recipes (1) and (2).
965  copy_data.cell_rhs(i) +=
966  scratch_data.pde_rhs_list[q_index] * // J_f
967  scratch_data.fe_values[VE].value(i, q_index) * // N_i
968  scratch_data.fe_values.JxW(q_index); // dV (dS in 2D)
969  break;
970  case 2:
971  case 3:
972  if (dim == 2) { // Integral I_b3-1 in recipe (2)
973  copy_data.cell_rhs(i) +=
974  scratch_data.pde_rhs_list[q_index][0] * // T
975  scratch_data.fe_values[VE].curl(i, q_index)[0] * // curl_s N_i
976  scratch_data.fe_values.JxW(q_index); // dS
977  } else if (dim == 3) { // Integral I_b3-1 in recipe (1) and (3).
978  copy_data.cell_rhs(i) +=
979  (scratch_data.pde_rhs_list[q_index][0] *
980  scratch_data.fe_values[VE].curl(i, q_index)[0] +
981  scratch_data.pde_rhs_list[q_index][1] *
982  scratch_data.fe_values[VE].curl(i, q_index)[1] +
983  scratch_data.pde_rhs_list[q_index][2] *
984  scratch_data.fe_values[VE].curl(i, q_index)[2]) *
985  scratch_data.fe_values.JxW(
986  q_index); // If recipe (1): T.(curl N_i)dV.
987  // If recipe (3): J_f.(curl N_i)dV.
988  // So, the scratch_data.pde_rhs_list must contain the values of T,
989  // or the values of J_f, depending on the context.
990  } else {
991  Assert(false, ExcInternalError());
992  }
993  break;
994  default:
995  Assert(false, ExcInternalError());
996  break;
997  }
998  }
999  }
1000 
1001  for (unsigned int f = 0; f < GeometryInfo<dim>::faces_per_cell; ++f) {
1002  scratch_data.do_robin = ((cell->face(f)->at_boundary()) &&
1003  (cell->face(f)->boundary_id() % 2 == 0) &&
1004  (cell->face(f)->boundary_id() != 0));
1005 
1006  scratch_data.do_K =
1007  ((cell->user_index() > 0) && (cell->face(f)->user_index() > 0));
1008 
1009  scratch_data.do_T_on_boundary =
1010  ((cell->face(f)->at_boundary()) && (scratch_data.type_of_pde_rhs == 3));
1011 
1012  Assert(!(scratch_data.do_robin && scratch_data.do_K), ExcInternalError());
1013 
1014  if (scratch_data.do_robin || scratch_data.do_K ||
1015  scratch_data.do_T_on_boundary) {
1016 
1017  scratch_data.fe_face_values.reinit(cell, f);
1018 
1019  if (scratch_data.do_robin) {
1020  scratch_data.gamma.value_list(
1021  scratch_data.fe_face_values.get_quadrature_points(),
1022  scratch_data.fe_face_values.get_normal_vectors(),
1023  cell->face(f)->boundary_id(),
1024  cell->material_id(),
1025  cell->user_index(),
1026  cell->face(f)->user_index(),
1027  scratch_data.gamma_list);
1028 
1029  scratch_data.robin_rhs.value_list(
1030  scratch_data.fe_face_values.get_quadrature_points(),
1031  scratch_data.fe_face_values.get_normal_vectors(),
1032  cell->face(f)->boundary_id(),
1033  cell->material_id(),
1034  cell->user_index(),
1035  cell->face(f)->user_index(),
1036  scratch_data.robin_rhs_list);
1037  }
1038 
1039  if (scratch_data.do_K) {
1040  scratch_data.free_surface_current.value_list(
1041  scratch_data.fe_face_values.get_quadrature_points(),
1042  scratch_data.fe_face_values.get_normal_vectors(),
1043  cell->material_id(),
1044  cell->user_index(),
1045  cell->face(f)->user_index(),
1046  scratch_data.free_surface_current_list);
1047  }
1048 
1049  if (scratch_data.do_T_on_boundary) {
1050  scratch_data.pde_rhs.value_list(
1051  scratch_data.fe_face_values.get_quadrature_points(),
1052  cell->material_id(),
1053  cell->user_index(),
1054  scratch_data.pde_rhs_list_face);
1055  }
1056 
1057  for (unsigned int q_index_face = 0;
1058  q_index_face < scratch_data.n_q_points_face;
1059  ++q_index_face) {
1060  for (unsigned int i = 0; i < scratch_data.dofs_per_cell; ++i) {
1061  if (scratch_data.do_robin) {
1062  for (unsigned int j = 0; j < scratch_data.dofs_per_cell; ++j) {
1063  if (dim == 2) {
1064  // Integral I_a2 in recipe (2).
1065  copy_data.cell_matrix(i, j) +=
1066  scratch_data.gamma_list[q_index_face] * // gamma
1067  (scratch_data.fe_face_values.normal_vector(q_index_face)[0] *
1068  scratch_data.fe_face_values[VE].value(i, q_index_face)[1] -
1069  scratch_data.fe_face_values.normal_vector(q_index_face)[1] *
1070  scratch_data.fe_face_values[VE].value(i,
1071  q_index_face)[0]) *
1072  (scratch_data.fe_face_values.normal_vector(q_index_face)[0] *
1073  scratch_data.fe_face_values[VE].value(j, q_index_face)[1] -
1074  scratch_data.fe_face_values.normal_vector(q_index_face)[1] *
1075  scratch_data.fe_face_values[VE].value(j,
1076  q_index_face)[0]) *
1077  scratch_data.fe_face_values.JxW(q_index_face); // dl
1078  } else if (dim == 3) {
1079  // Integral I_a2 in recipe (1).
1080  copy_data.cell_matrix(i, j) +=
1081  scratch_data.gamma_list[q_index_face] * // gamma
1082  ((scratch_data.fe_face_values.normal_vector(q_index_face)[1] *
1083  scratch_data.fe_face_values[VE].value(i,
1084  q_index_face)[2] -
1085  scratch_data.fe_face_values.normal_vector(q_index_face)[2] *
1086  scratch_data.fe_face_values[VE].value(i,
1087  q_index_face)[1]) *
1088  (scratch_data.fe_face_values.normal_vector(
1089  q_index_face)[1] *
1090  scratch_data.fe_face_values[VE].value(j,
1091  q_index_face)[2] -
1092  scratch_data.fe_face_values.normal_vector(
1093  q_index_face)[2] *
1094  scratch_data.fe_face_values[VE].value(
1095  j, q_index_face)[1]) +
1096  (scratch_data.fe_face_values.normal_vector(q_index_face)[0] *
1097  scratch_data.fe_face_values[VE].value(i,
1098  q_index_face)[2] -
1099  scratch_data.fe_face_values.normal_vector(q_index_face)[2] *
1100  scratch_data.fe_face_values[VE].value(i,
1101  q_index_face)[0]) *
1102  (scratch_data.fe_face_values.normal_vector(
1103  q_index_face)[0] *
1104  scratch_data.fe_face_values[VE].value(j,
1105  q_index_face)[2] -
1106  scratch_data.fe_face_values.normal_vector(
1107  q_index_face)[2] *
1108  scratch_data.fe_face_values[VE].value(
1109  j, q_index_face)[0]) +
1110  (scratch_data.fe_face_values.normal_vector(q_index_face)[0] *
1111  scratch_data.fe_face_values[VE].value(i,
1112  q_index_face)[1] -
1113  scratch_data.fe_face_values.normal_vector(q_index_face)[1] *
1114  scratch_data.fe_face_values[VE].value(i,
1115  q_index_face)[0]) *
1116  (scratch_data.fe_face_values.normal_vector(
1117  q_index_face)[0] *
1118  scratch_data.fe_face_values[VE].value(j,
1119  q_index_face)[1] -
1120  scratch_data.fe_face_values.normal_vector(
1121  q_index_face)[1] *
1122  scratch_data.fe_face_values[VE].value(
1123  j, q_index_face)[0])) *
1124  scratch_data.fe_face_values.JxW(
1125  q_index_face); // (n x N_i) . (n x N_j) dS
1126  } else {
1127  Assert(false, ExcInternalError());
1128  }
1129  }
1130 
1131  // Integral I_b1 in recipes (1), (2), and (3).
1132  copy_data.cell_rhs(i) +=
1133  -scratch_data.robin_rhs_list[q_index_face] * // Q
1134  scratch_data.fe_face_values[VE].value(i, q_index_face) * // N_i
1135  scratch_data.fe_face_values.JxW(q_index_face); // dS (dl in 2D)
1136  } // if (scratch_data.do_robin)
1137 
1138  if (scratch_data.do_K) {
1139  // Integral I_b2 in recipes (1) and (2).
1140  copy_data.cell_rhs(i) +=
1141  scratch_data.free_surface_current_list[q_index_face] * // K_f
1142  scratch_data.fe_face_values[VE].value(i, q_index_face) * // N_i
1143  scratch_data.fe_face_values.JxW(q_index_face); // dS (dl in 2D)
1144  }
1145 
1146  if (scratch_data.do_T_on_boundary) {
1147  if (dim == 2) {
1148  // Integral I_b3-2 in recipe (2).
1149  copy_data.cell_rhs(i) -=
1150  scratch_data.pde_rhs_list_face[q_index_face][0] * // T
1151  (scratch_data.fe_face_values.normal_vector(q_index_face)[0] *
1152  scratch_data.fe_face_values[VE].value(i, q_index_face)[1] -
1153  scratch_data.fe_face_values.normal_vector(q_index_face)[1] *
1154  scratch_data.fe_face_values[VE].value(i,
1155  q_index_face)[0] // S
1156  ) * // n x N_i
1157  scratch_data.fe_face_values.JxW(q_index_face); // dl
1158  } else if (dim == 3) {
1159  // Integral I_b3-2 in recipes (1) and (3).
1160  copy_data.cell_rhs(i) -=
1161  (scratch_data.pde_rhs_list_face[q_index_face][0] *
1162  (scratch_data.fe_face_values.normal_vector(q_index_face)[1] *
1163  scratch_data.fe_face_values[VE].value(i,
1164  q_index_face)[2] -
1165  scratch_data.fe_face_values.normal_vector(q_index_face)[2] *
1166  scratch_data.fe_face_values[VE].value(i,
1167  q_index_face)[1]) -
1168  scratch_data.pde_rhs_list_face[q_index_face][1] *
1169  (scratch_data.fe_face_values.normal_vector(q_index_face)[0] *
1170  scratch_data.fe_face_values[VE].value(i,
1171  q_index_face)[2] -
1172  scratch_data.fe_face_values.normal_vector(q_index_face)[2] *
1173  scratch_data.fe_face_values[VE].value(i,
1174  q_index_face)[0]) +
1175  scratch_data.pde_rhs_list_face[q_index_face][2] *
1176  (scratch_data.fe_face_values.normal_vector(q_index_face)[0] *
1177  scratch_data.fe_face_values[VE].value(i,
1178  q_index_face)[1] -
1179  scratch_data.fe_face_values.normal_vector(q_index_face)[1] *
1180  scratch_data.fe_face_values[VE].value(i,
1181  q_index_face)[0])) *
1182  scratch_data.fe_face_values.JxW(
1183  q_index_face); // If recipe (1): T.(n x N_i)dS.
1184  } // If recipe (3): J_f.(n x N_i)dS.
1185  // So, the scratch_data.pde_rhs_list_face must contain the values
1186  // of T or the values of J_f, depending on the context.
1187  else {
1188  Assert(false, ExcInternalError());
1189  }
1190  }
1191  }
1192  }
1193  }
1194  }
1195  cell->get_dof_indices(copy_data.local_dof_indices);
1196 }
1197 
1198 template<int dim, int stage>
1199 void
1200 Solver1<dim, stage>::copy_local_to_global(const AssemblyCopyData& copy_data)
1201 {
1202  constraints.distribute_local_to_global(copy_data.cell_matrix,
1203  copy_data.cell_rhs,
1204  copy_data.local_dof_indices,
1205  system_matrix,
1206  system_rhs);
1207 }
1208 
1209 template<int dim, int stage>
1210 void
1212 {
1213  Weight<dim, stage> weight;
1214  const Function<dim, double>* mask = &weight;
1215 
1216  Constants::QuadratureTableVector<dim> qt(dof_handler.get_fe().degree - 1);
1217  QGauss<dim> quadrature(qt.enorm());
1218 
1219  VectorTools::integrate_difference(MappingQ<dim>(mapping_degree),
1220  dof_handler,
1221  solution,
1222  *exact_solution,
1223  L2_per_cell,
1224  quadrature,
1225  VectorTools::L2_norm,
1226  mask);
1227 
1228  L2_norm = VectorTools::compute_global_error(
1229  triangulation, L2_per_cell, VectorTools::L2_norm);
1230 
1231  VectorTools::integrate_difference(MappingQ<dim>(mapping_degree),
1232  dof_handler,
1233  solution,
1234  *exact_solution,
1235  Linfty_per_cell,
1236  QGauss<dim>(1),
1237  VectorTools::Linfty_norm,
1238  mask);
1239 
1240  Linfty_norm = Linfty_per_cell.linfty_norm();
1241 }
1242 
1243 template<int dim, int stage>
1244 void
1246 {
1247  Constants::QuadratureTableVector<dim> qt(fe.degree - 1);
1248 
1249  AffineConstraints<double> constraints_empty;
1250  constraints_empty.close();
1251 
1252  VectorTools::project(MappingQ<dim>(mapping_degree),
1253  dof_handler,
1254  constraints_empty,
1255  QGauss<dim>(qt.sim()),
1256  *exact_solution,
1257  projected_exact_solution);
1258 }
1259 
1260 template<int dim, int stage>
1261 void
1263 {
1264  std::vector<std::string> solution_names(dim, "VectorField");
1265  std::vector<DataComponentInterpretation::DataComponentInterpretation>
1266  interpretation(dim,
1267  DataComponentInterpretation::component_is_part_of_vector);
1268 
1269  DataOut<dim> data_out;
1270 
1271  data_out.add_data_vector(
1272  dof_handler, solution, solution_names, interpretation);
1273 
1274  if (project_exact_solution && exact_solution) {
1275  std::vector<std::string> solution_names_ex(dim, "VectorFieldExact");
1276 
1277  data_out.add_data_vector(
1278  dof_handler, projected_exact_solution, solution_names_ex, interpretation);
1279  }
1280 
1281  if (exact_solution) {
1282  data_out.add_data_vector(L2_per_cell, "L2norm");
1283  data_out.add_data_vector(Linfty_per_cell, "LinftyNorm");
1284  }
1285 
1286  std::ofstream ofs;
1287 
1288  if (write_higher_order_cells) {
1289  DataOutBase::VtkFlags flags;
1290  flags.write_higher_order_cells = true;
1291  data_out.set_flags(flags);
1292 
1293  const MappingQ<dim> mapping(mapping_degree);
1294 
1295  data_out.build_patches(mapping,
1296  fe.degree + 2,
1297  DataOut<dim>::CurvedCellRegion::curved_inner_cells);
1298 
1299  ofs.open(fname + ".vtu");
1300  data_out.write_vtu(ofs);
1301 
1302  } else {
1303 
1304  data_out.build_patches();
1305 
1306  ofs.open(fname + ".vtk");
1307  data_out.write_vtk(ofs);
1308  }
1309 
1310  ofs.close();
1311 }
1312 
1313 template<int dim, int stage>
1314 void
1316 {
1317  std::ofstream ofs_matrix(fname + "_matrix.csv");
1318  std::ofstream ofs_rhs(fname + "_rhs.csv");
1319 
1320  for (unsigned int i = 0; i < system_matrix.m(); ++i) {
1321  ofs_rhs << system_rhs(i);
1322  if (i < (system_matrix.m() - 1))
1323  ofs_rhs << "\n";
1324 
1325  for (unsigned int j = 0; j < system_matrix.n(); ++j) {
1326  ofs_matrix << std::scientific << std::setprecision(16)
1327  << system_matrix.el(i, j);
1328 
1329  if (j < (system_matrix.m() - 1))
1330  ofs_matrix << ", ";
1331  }
1332  if (i < (system_matrix.m() - 1))
1333  ofs_matrix << "\n";
1334  }
1335 
1336  ofs_rhs.close();
1337  ofs_matrix.close();
1338 }
1339 
1340 } // namespace StaticVectorSolver
1341 
1342 #endif
The tables that contain the amount of quadrature points used in vector problems.
Definition: constants.hpp:101
unsigned int enorm() const
Returns the amount of quadrature points used when calculating the error norms.
unsigned int sim() const
Returns the amount of quadrature points used when assembling system if linear equations.
Implements the coefficient on the left-hand side of the Robin boundary condition (iii) of the static...
Implements the source vector field on the right-hand side of the partial differential equation (i) of...
Implements the vector field, , on the right-hand side of the Robin boundary condition (iii) of the st...
Solves static vector boundary value problem.
unsigned int get_n_dofs() const
Returns the total amount of the degrees of freedom.
void assemble()
Assembles the system matrix and the right-hand side vector.
SparsityPattern sparsity_pattern
The sparsity pattern of the system matrix.
void project_exact_solution_fcn()
Projects exact solution.
void compute_error_norms()
Computes error norms.
unsigned int get_mapping_degree() const
Returns degree of the interpolating Lagrange polynomials used for mapping from the reference cell to ...
void save_matrix_and_rhs_to_csv(std::string fname) const
Saves the system matrix and the right-hand side into a csv file.
double get_L2_norm() const
Returns error norm.
double get_Linfty_norm() const
Returns error norm.
const FE_Nedelec< dim > fe
The finite elements.
void save() const
Saves simulation results into a vtk or vtu file.
virtual void fill_dirichlet_stack()=0
Initializes the data member StaticVectorSolver::Solver1::dirichlet_stack.
DoFHandler< dim > dof_handler
The dof handler.
virtual void solve()=0
Solves the system of linear equations.
AffineConstraints< double > constraints
The constraints associated with the Dirichlet boundary conditions.
Vector< double > solution
The solution vector, that is, degrees of freedom yielded by the simulation.
SparseMatrix< double > system_matrix
The system matrix.
Solver1(unsigned int p, unsigned int mapping_degree, unsigned int type_of_pde_rhs=3, double eta_squared=0.0, std::string fname="data", const Function< dim > *exact_solution=nullptr, bool print_time_tables=false, bool project_exact_solution=false, bool write_higher_order_cells=false)
The only constructor.
virtual void make_mesh()=0
Initializes the data member StaticVectorSolver::Solver1::triangulation.
Vector< double > system_rhs
The system right-hand side vector.
Triangulation< dim > triangulation
The mesh.
unsigned int get_n_cells() const
Returns the number of active cells in the mesh.
void setup()
Initializes system matrix and the right-hand side vector.
void run()
Runs the simulation.
std::map< types::boundary_id, const Function< dim > * > dirichlet_stack
A map that contains pairs of boundary IDs and the corresponding Dirichlet boundary conditions....
const DoFHandler< dim > & get_dof_handler() const
Returns a reference to dof handler.
const Vector< double > & get_solution() const
Returns a reference to solution.
const Triangulation< dim > & get_tria() const
Returns a reference to triangulation.
Vector< double > projected_exact_solution
The projected exact solution vector.
void clear()
Releases computer memory associated with system matrix and right-hand side.
unsigned int get_rhs_type() const
Returns the value of type_of_pde_rhs.
Implements the permeability, , in the partial differential equation (i) of the vector boundary value ...
Implements the weight function for calculating the and error norms.