deal.II version GIT relicensing-6809-ge913b9bb34 2026-09-25 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
step-98.h
Go to the documentation of this file.
1
1497 *   const types::material_id material_id_free_space =
1498 *   1; // Material ID of the free space.
1499 *   const types::material_id material_id_core =
1500 *   2; // Material ID of the magnetic core.
1501 *   const types::material_id material_id_free_current =
1502 *   3; // Material ID of the free-current region.
1503 *  
1504 *   const types::boundary_id outer_boundary_id = 1; // Boundary ID.
1505 *  
1506 *   const types::manifold_id spherical_manifold_id =
1507 *   1; /* The ID of the manifold assigned to all of the mesh except for the
1508 *   magnetic core. */
1510 *   2; /* The ID of the manifold assigned to the square region in the middle
1511 *   of the mesh. */
1512 *   const types::manifold_id transfinite_interpolation_manifold_id =
1513 *   3; /* The ID of the manifold assigned to the cells in between the square
1514 *   region in the middle of the mesh and the innermost circular interface
1515 *   (the boundary of the magnetic core). */
1516 *  
1517 *   const unsigned int mapping_degree = 2; // Mapping degree used in all solvers.
1518 *   const unsigned int fe_degree = 0; // Degree of the finite elements.
1519 *  
1520 *   const double eta_squared = 0.0; // eta^2 when solving for A.
1521 *  
1522 *   const unsigned int n_threads_max = 0; // If >0 limits the number of threads.
1523 *  
1524 *   const bool project_exact_solution = false; // Save the exact solution.
1525 *   } // namespace Settings
1526 *  
1527 * @endcode
1528 *
1529 *
1530 * <a name="step_98-Convergencetable"></a>
1531 * <h3>Convergence table</h3>
1532 * The following class describes a convergence table. The convergence tables are
1533 * saved on disk in TeX format.
1534 *
1535 * @code
1536 *   class MainOutputTable : public ConvergenceTable
1537 *   {
1538 *   public:
1539 *   MainOutputTable() = delete;
1540 *  
1541 *   MainOutputTable(const unsigned int dim)
1542 *   : ConvergenceTable()
1543 *   , dim(dim)
1544 *   {}
1545 *  
1546 *   void save(const std::string &file_name)
1547 *   {
1548 *   set_precision("L2", 2);
1549 *  
1550 *   set_scientific("L2", true);
1551 *  
1553 *   "ncells",
1555 *   dim);
1556 *  
1557 *   set_tex_caption("p", "p");
1558 *   set_tex_caption("r", "r");
1559 *   set_tex_caption("ncells", "nr. cells");
1560 *   set_tex_caption("ndofs", "nr. dofs");
1561 *   set_tex_caption("L2", "L2 norm");
1562 *  
1563 *   set_column_order({"p", "r", "ncells", "ndofs", "L2"});
1564 *  
1565 *   std::ofstream ofs(file_name + ".tex");
1566 *   write_tex(ofs);
1567 *   }
1568 *  
1569 *   private:
1570 *   const unsigned int dim;
1571 *   };
1572 *  
1573 * @endcode
1574 *
1575 *
1576 * <a name="step_98-Equations"></a>
1577 * <h3>Equations</h3>
1578 * The following namespace contains closed-form analytical expressions
1579 * for @f$T@f$, @f$\vec{J}_f@f$, @f$\vec{A}@f$, @f$B@f$, mentioned in the introduction to this
1580 * tutorial.
1581 *
1582 * @code
1583 *   namespace ExactSolutions
1584 *   {
1585 *  
1586 * @endcode
1587 *
1588 * The following function describes the free-current density, @f$\vec{J}_f@f$,
1589 * inside the current region. The current density in this tutorial is
1590 * implemented by `ExactSolutions::FreeCurrentDensity` class and by
1591 * `SolverT::Solver::free_current_density` member function . There is a
1592 * subtle difference in how these two implementation compute the free-current
1593 * density. Both classes, however, utilize the same expression for the
1594 * free-current density. This function describes the expression.
1595 *
1596 * @code
1597 *   inline Tensor<1, 2> volume_free_current_density(const Point<2> &p,
1598 *   const double K0)
1599 *   {
1600 *   return Tensor<1, 2>({-K0 * p[1], K0 * p[0]});
1601 *   }
1602 *  
1603 * @endcode
1604 *
1605 * The following class implements the closed-form analytical
1606 * [expression](@ref Step98_Equation_1_Jf) for the free-current density,
1607 * @f$\vec{J}_f@f$, in the entire domain. The free-current density is computed
1608 * purely on the basis of the spatial coordinates of the field point. In go
1609 * coordinates, out comes the free-current density. The information on the
1610 * material ID of the mesh cells and any other information on the mesh is
1611 * ignored. This function is used for computing @f$L_2@f$ error norms and for
1612 * computing the projected exact solution. The @f$\vec{J}_f@f$ on the right-hand
1613 * side of the div-grad equation is implemented by the member function
1614 * `SolverT::Solver::free_current_density`
1615 *
1616 * @code
1617 *   class FreeCurrentDensity : public Function<2>
1618 *   {
1619 *   public:
1620 *   FreeCurrentDensity()
1621 *   : Function<2>(2)
1622 *   {}
1623 *  
1624 *   virtual void
1625 *   vector_value_list(const std::vector<Point<2>> &p,
1626 *   std::vector<Vector<double>> &values) const override final
1627 *   {
1628 *   Assert(values.size() == p.size(),
1629 *   ExcDimensionMismatch(values.size(), p.size()));
1630 *  
1631 *   for (unsigned int i = 0; i < values.size(); i++)
1632 *   {
1633 *   const double r = p[i].norm();
1634 *  
1635 *   if ((r >= Settings::a2) && (r <= Settings::b2))
1636 *   {
1637 *   const Tensor<1, 2> Jf =
1638 *   volume_free_current_density(p[i], Settings::K0);
1639 *  
1640 *   values[i][0] = Jf[0];
1641 *   values[i][1] = Jf[1];
1642 *   }
1643 *   else
1644 *   values[i] = 0;
1645 *   }
1646 *   }
1647 *   };
1648 *  
1649 * @endcode
1650 *
1651 * The following class implements the closed-form analytical
1652 * [expression](@ref Step98_Equation_4_A)
1653 * for the magnetic vector potential, @f$\vec{A}@f$.
1654 *
1655 * @code
1656 *   class MagneticVectorPotential : public Function<2>
1657 *   {
1658 *   public:
1659 *   MagneticVectorPotential()
1660 *   : Function<2>(2)
1661 *   {}
1662 *  
1663 *   virtual void
1664 *   vector_value_list(const std::vector<Point<2>> &p,
1665 *   std::vector<Vector<double>> &values) const override final
1666 *   {
1667 *   Assert(values.size() == p.size(),
1668 *   ExcDimensionMismatch(values.size(), p.size()));
1669 *  
1670 *   for (unsigned int i = 0; i < values.size(); i++)
1671 *   {
1672 *   const double r = p[i].norm();
1673 *   double A;
1674 *  
1675 *   using namespace Settings;
1676 *  
1677 *   if (r < b1)
1678 *   {
1679 *   A = (mu_1 * K0 / 4.0) * (b2 * b2 - a2 * a2);
1680 *   }
1681 *   else if (r < a2)
1682 *   {
1683 *   A = (mu_0 * K0 / 4.0) * (b2 * b2 - a2 * a2) *
1684 *   (r + b1 * b1 * (mu_r - 1.0) / r) / r;
1685 *   }
1686 *   else if (r < b2)
1687 *   {
1688 *   A = (mu_0 * K0 / 2.0) *
1689 *   (b2 * b2 * r / 2.0 - std::pow(r, 3) / 4.0 +
1690 *   (a2 * (b2 * b2 - a2 * a2) *
1691 *   (a2 + b1 * b1 * (mu_r - 1.0) / a2) / 2.0 -
1692 *   std::pow(b2 * a2, 2) / 2.0 + std::pow(a2, 4) / 4.0) /
1693 *   r) /
1694 *   r;
1695 *   }
1696 *   else
1697 *   {
1698 *   A = (mu_0 * K0 / 2.0) * b2 *
1699 *   (std::pow(b2, 3) / 4.0 +
1700 *   (a2 * (b2 * b2 - a2 * a2) *
1701 *   (a2 + b1 * b1 * (mu_r - 1.0) / a2) / 2.0 -
1702 *   std::pow(b2 * a2, 2) / 2.0 + std::pow(a2, 4) / 4.0) /
1703 *   b2) /
1704 *   (r * r);
1705 *   }
1706 *  
1707 *   values[i][0] = -A * p[i][1];
1708 *   values[i][1] = A * p[i][0];
1709 *   }
1710 *   }
1711 *   };
1712 *  
1713 * @endcode
1714 *
1715 * The following class implements the closed-form analytical
1716 * [expression](@ref Step98_Equation_3_T) for
1717 * the current vector potential, @f$T@f$.
1718 *
1719 * @code
1720 *   class CurrentVectorPotential : public Function<2>
1721 *   {
1722 *   public:
1723 *   CurrentVectorPotential()
1724 *   : Function<2>()
1725 *   {}
1726 *  
1727 *   virtual void
1728 *   value_list(const std::vector<Point<2>> &p,
1729 *   std::vector<double> &values,
1730 *   const unsigned int component = 0) const override final
1731 *   {
1732 *   Assert(values.size() == p.size(),
1733 *   ExcDimensionMismatch(values.size(), p.size()));
1734 *  
1735 *   Assert(component == 0,
1736 *   ExcMessage("This line is to avoid compiler warnings."));
1737 *  
1738 *   for (unsigned int i = 0; i < values.size(); i++)
1739 *   {
1740 *   const double r = p[i].norm();
1741 *   using namespace Settings;
1742 *  
1743 *   if (r < a2)
1744 *   {
1745 *   values[i] = K0 * (b2 * b2 - a2 * a2) / 2.0;
1746 *   }
1747 *   else if (r < b2)
1748 *   {
1749 *   values[i] = -K0 * (r * r - b2 * b2) / 2.0;
1750 *   }
1751 *   else
1752 *   {
1753 *   values[i] = 0.0;
1754 *   }
1755 *   }
1756 *   }
1757 *   };
1758 *  
1759 * @endcode
1760 *
1761 * The following class implements the closed-form analytical
1762 * [expression](@ref Step98_Equation_2_B)
1763 * for the magnetic field, @f$B@f$.
1764 *
1765 * @code
1766 *   class MagneticField : public Function<2>
1767 *   {
1768 *   public:
1769 *   MagneticField()
1770 *   : Function<2>()
1771 *   {}
1772 *  
1773 *   virtual void
1774 *   value_list(const std::vector<Point<2>> &p,
1775 *   std::vector<double> &values,
1776 *   const unsigned int component = 0) const override final
1777 *   {
1778 *   Assert(values.size() == p.size(),
1779 *   ExcDimensionMismatch(values.size(), p.size()));
1780 *  
1781 *   Assert(component == 0,
1782 *   ExcMessage("This line is to avoid compiler warnings."));
1783 *  
1784 *   for (unsigned int i = 0; i < values.size(); i++)
1785 *   {
1786 *   const double r = p[i].norm();
1787 *   using namespace Settings;
1788 *  
1789 *   if (r < b1)
1790 *   {
1791 *   values[i] = mu_1 * K0 * (b2 * b2 - a2 * a2) / 2.0;
1792 *   }
1793 *   else if (r < a2)
1794 *   {
1795 *   values[i] = mu_0 * K0 * (b2 * b2 - a2 * a2) / 2.0;
1796 *   }
1797 *   else if (r < b2)
1798 *   {
1799 *   values[i] = mu_0 * K0 * (b2 * b2 - r * r) / 2.0;
1800 *   }
1801 *   else
1802 *   {
1803 *   values[i] = 0.0;
1804 *   }
1805 *   }
1806 *   }
1807 *   };
1808 *  
1809 *   } // namespace ExactSolutions
1810 *  
1811 * @endcode
1812 *
1813 *
1814 * <a name="step_98-BaseSolver"></a>
1815 * <h3>Base Solver</h3>
1816 *
1817
1818 *
1819 * As discussed above, the following namespace aggregates the code common to all
1820 * four solvers used in the tutorial. All four solvers are derived from the
1821 * `BaseSolver` class.
1822 *
1823 * @code
1824 *   namespace BaseClasses
1825 *   {
1826 * @endcode
1827 *
1828 * The computation of the integrals of the functionals is delegated to the
1829 * derived classes. The function that computes the integrals,
1830 * `BaseSolver::system_matrix_local`, is virtual and must be overridden by
1831 * the derived classes. However, the objects of the types FEValues are
1832 * initialized at the level of the `BaseSolver` class together with
1833 * `AssemblyScratchData`. For the initialization to work properly, the
1834 * `BaseSolver` class must know which cell data to compute for a particular
1835 * implementation of the solver down the hierarchy. This information is
1836 * communicated to the `BaseSolver` by passing an argument of the type
1837 * `UpdateFlagsCollection` to the constructor. In all solvers, with exception
1838 * of `SolverT`, we have two types of finite elements. One type of finite
1839 * elements models the solution to the partial differential equation. Another
1840 * models the physical quantity on the right-hand side of the partial
1841 * differential equation. The `solution_update_flags` data member below
1842 * contains the flags for updating the values of the finite elements that
1843 * model the solution. The `rhs_update_flags` data member contains the flags
1844 * for updating the values of the finite elements that model the physical
1845 * quantity on the right-hand side of the partial differential equation.
1846 *
1847 * @code
1848 *   struct UpdateFlagsCollection
1849 *   {
1850 *   UpdateFlags solution_update_flags;
1851 *   UpdateFlags rhs_update_flags;
1852 *   };
1853 *  
1854 * @endcode
1855 *
1856 * Each iteration of the program consists of
1857 * [four stages](@ref Step98_FourStages). Each stage utilizes one solver. All
1858 * four solvers used in this tutorial are derived from the following class.
1859 * The solver used in the first stage loads the mesh and refines it if
1860 * necessary. The solvers in the other three stages reuse the mesh prepared at
1861 * the first stage. Furthermore, the solver in the first stage expects a
1862 * closed-form analytical expression on the right-hand side of the partial
1863 * differential equation. Each solver in the other three stages expects a
1864 * potential computed at one of the preceding stages, i.e., a field in a form
1865 * of linear superposition of the shape functions. At the top of the following
1866 * class, we declare two constructors. The first constructor must be used for
1867 * constructing solver for the first stage. The second constructor must be
1868 * used for constructing the solvers for the second, third, and fourth stages.
1869 * The second constructor has three extra arguments, `triangulation_rhs`,
1870 * `dof_handler_rhs`, and `solution_rhs` for accommodating the mesh and the
1871 * potential computed at one of the preceding stages.
1872 *
1873
1874 *
1875 * Following the constructors are the eight functions that implement various
1876 * steps typical for every solver. The function `run` aggregates these steps.
1877 * This arrangement of functions is quite standard in deal.II, see @ref step_3 "step-3",
1878 * for instance. Normally, the `setup` function begins by distributing the
1879 * dofs. The problem is: the type of the finite elements is not known in the
1880 * `BaseSolver` class. It is specified in the derived classes. Therefore, it
1881 * could be reasonable to make the setup function virtual as well. Instead,
1882 * we move the dof distribution code to the end of the `make_mesh` function
1883 * which is, in fact, virtual and must be overridden in the derived classes
1884 * anyway.
1885 *
1886
1887 *
1888 * After that, we declare six get functions that provide access to protected
1889 * data. These functions are called from outside the solver, see
1890 * `MagneticProblem::run` function. The data provided by these get-functions
1891 * is used to fill the convergence tables and to pass the references to the
1892 * triangulation, the dof handler, and the dofs between solvers.
1893 *
1894 * @code
1895 *   class BaseSolver
1896 *   {
1897 *   public:
1898 *   BaseSolver(const unsigned int mapping_degree,
1899 *   const UpdateFlagsCollection update_flags_collection,
1900 *   const std::string &file_name,
1901 *   const Function<2> *exact_solution);
1902 *  
1903 *   BaseSolver(const Triangulation<2> &triangulation_rhs,
1904 *   const DoFHandler<2> &dof_handler_rhs,
1905 *   const Vector<double> &solution_rhs,
1906 *   const unsigned int stage,
1907 *   const unsigned int mapping_degree,
1908 *   const UpdateFlagsCollection update_flags_collection,
1909 *   const std::string &file_name,
1910 *   const Function<2> *exact_solution);
1911 *  
1912 *   virtual void make_mesh() = 0; /* If used in the first stage, loads the mesh
1913 *   and distributes the dofs. In other stages
1914 *   just distributes the dofs.*/
1915 *   void setup(); /* Applies Dirichlet boundary condition, setups the
1916 *   dof pattern, and initializes vectors, matrices. */
1917 *   void assemble(); // Assembles the system of linear equations.
1918 *   void solve(); // Solves the system of linear equations.
1919 *   void output_results() const; // Saves the result into a `.vtu` file.
1920 *   void compute_error_norms(); // Computes L^2 error norm.
1921 *   void project_exact_solution_fcn(); // Projects exact solution.
1922 *   void clear(); // Clears the memory for the next solver.
1923 *   void run(); /* Executes the last eight functions in the proper order
1924 *   and measures the execution time for each function. */
1925 *  
1926 *   double get_L2_norm() const;
1927 *   unsigned int get_n_cells() const;
1928 *   types::global_dof_index get_n_dofs() const;
1929 *   const Triangulation<2> &get_tria() const;
1930 *   const DoFHandler<2> &get_dof_handler() const;
1931 *   const Vector<double> &get_solution() const;
1932 *  
1933 * @endcode
1934 *
1935 * We begin the `protected` section of the `BaseSolver` class by declaring
1936 * three data members that store the input from one of the preceding
1937 * solvers. If there is no preceding solver and these data is not provided,
1938 * i.e., first of the two constructors above has been used, these three data
1939 * members point to `triangulation`, `dof_handler`, and `solution`, of the
1940 * current solver.
1941 *
1942
1943 *
1944 * Following are the three data members that store the triangulation, dof
1945 * handler, and dofs vector of the current solver. In the case of the
1946 * first-stage solver, the `triangulation` data member stores the loaded and
1947 * refined mesh. This data member is not used in the case of the solvers of
1948 * the second, third, and fourth stages. The `dof_handler` and `solution`
1949 * represent the result of the solver, i.e., the computed potential or
1950 * field.
1951 *
1952
1953 *
1954 * Next, we declare four data members that describe the system of linear
1955 * equations to be solved by the linear solver. The components of the system
1956 * matrix and that of the right-hand side are computed by the `assemble`
1957 * function. The affine constraints are used to apply the Dirichlet boundary
1958 * conditions and to distribute the local (cell specific) system matrix and
1959 * right-hand side to `system_matrix` and `system_rhs`. The are no hanging
1960 * nodes and hanging node constraints in this program. The last data member
1961 * in this block describes the dynamic sparsity pattern. The tutorial @ref step_2 "step-2"
1962 * discusses the rationale behind the dynamic sparsity pattern.
1963 *
1964
1965 *
1966 * The next block contains two data members related to the exact solution.
1967 * The data member `exact_solution` points to the closed-form analytical
1968 * solution the solver attempts to compute.
1969 * If `Settings::project_exact_solution=true`, the exact solution is
1970 * projected onto a proper function space and the data member
1971 * `projected_exact_solution` is populated by the dofs of the projected
1972 * exact solution. The corresponding dof handler is `dof_handler`. Together
1973 * `dof_handler` and `projected_exact_solution` constitute the field
1974 * function which describes the exact solution. It is saved into the `.vtu`
1975 * file next to the solution and the @f$L_2@f$ error norm.
1976 *
1977
1978 *
1979 * The next four data members simply store the data supplied as arguments
1980 * to the constructor. The `stage` data member stores the number of the
1981 * current stage. The `mapping_degree` data member contains the degree of
1982 * mapping from the reference cell to a mesh cell and back. The
1983 * `update_flags_collection` contains the information on which finite
1984 * element values must be computed for each cell. The names of the output
1985 * files are derived by appending strings to `file_name`.
1986 *
1987
1988 *
1989 * The next block contains two data members computed by the function
1990 * `BaseSolver::compute_error_norms`. The `L2_per_cell` data member
1991 * contains one value of the @f$L^2@f$ error norm per mesh cell. It is saved
1992 * into the `.vtu` file next to the solution. The `L2_norm` data member
1993 * contains one value of the @f$L^2@f$ error norm per mesh. It is reported in
1994 * the convergence table.
1995 *
1996 * @code
1997 *   protected:
1998 *   const Triangulation<2> &triangulation_rhs;
1999 *   const DoFHandler<2> &dof_handler_rhs;
2000 *   const Vector<double> &solution_rhs;
2001 *  
2002 *   Triangulation<2> triangulation;
2003 *   DoFHandler<2> dof_handler;
2004 *   Vector<double> solution;
2005 *  
2006 *   SparseMatrix<double> system_matrix;
2007 *   Vector<double> system_rhs;
2008 *   AffineConstraints<double> constraints;
2009 *   SparsityPattern sparsity_pattern;
2010 *  
2011 *   const Function<2> *exact_solution;
2012 *   Vector<double> projected_exact_solution;
2013 *  
2014 *   const unsigned int stage;
2015 *   const unsigned int mapping_degree;
2016 *   const UpdateFlagsCollection update_flags_collection;
2017 *   const std::string file_name;
2018 *  
2019 *   Vector<double> L2_per_cell;
2020 *   double L2_norm;
2021 *  
2022 * @endcode
2023 *
2024 * The program utilizes the WorkStream technology. The @ref step_9 "step-9" tutorial
2025 * does a much better job of explaining the workings of WorkStream.
2026 * Reading the @ref workstream_paper "WorkStream paper" is recommended.
2027 * In very simple terms, the workings of the WorkStream can be envisioned as
2028 * the following. Let us assume we have a task of computing components of
2029 * the system matrix, @f$A_{ij}@f$, and the right-hand side vector, @f$b_i@f$.
2030 * Simply put, we need to fill in the matrix `system_matrix` and vector
2031 * `system_rhs`. This is a big task as the number of dofs is large. The
2032 * idea is to split the big task on a number of small tasks and feed them
2033 * to multiple threads to speed up the calculation process. Each small
2034 * task consists of computing the contributions of a single mesh cell to
2035 * `system_matrix` and `system_rhs`. These contributions are stored
2036 * temporary in `cell_matrix` and `cell_rhs` for each cell. These
2037 * contributions are then copied to `system_matrix` and `system_rhs`.
2038 * WorkStream creates and schedules the small tasks and takes care
2039 * of copying `cell_matrix` and `cell_rhs` to `system_matrix` and
2040 * `system_rhs`. The rest of the declarations in the `protected` section
2041 * of the `BaseSolver` class help to communicate to WorkStream information
2042 * which is necessary for its operation.
2043 *
2044
2045 *
2046 * First, the `CellIteratorPair` type is declared in the block of code
2047 * below. The solvers in the second, third, and fourth stages use two dof
2048 * handlers, `dof_handler` and `dof_handler_rhs`. The WorkStream needs to
2049 * walk through the two dof handlers synchronously. For this purpose we
2050 * pair two active cell iterators (one from `dof_handler`, another from
2051 * `dof_handler_rhs`). For that we need the `CellIteratorPair` type. The
2052 * solver at the first stage (a solver constructed by invoking the first
2053 * constructor above) uses only one dof handler. In this case the
2054 * constructor makes `dof_handler_rhs` to reference `dof_handler`. In
2055 * effect, both iterators of the tuple will iterate the same dof handler.
2056 * In the case of the first-stage solver we will use only the first
2057 * iterator.
2058 *
2059
2060 *
2061 * Next, we declare the `AssemblyScratchData` type. An object of this type
2062 * contains the relevant information on the current mesh cell which is used
2063 * as an input for computing the components of the system matrix and the
2064 * right-hand side. WorkStream creates an object of this type and passes
2065 * it to the function `system_matrix_local` which, in turn, computes
2066 * components of `cell_matrix` and `cell_rhs`.
2067 *
2068
2069 *
2070 * Next, the type `AssemblyCopyData` is declared. An objects of this type
2071 * contains the cell specific contributions to the system matrix and the
2072 * right-hand side. The WorkStream creates object of this type and passes
2073 * it to function `system_matrix_local` along with an object of the type
2074 * `AssemblyScratchData`. The function `system_matrix_local`, in turn,
2075 * takes input data form the object of the type `AssemblyScratchData`,
2076 * computes the relevant integrals and places the result into the object
2077 * of the type `AssemblyCopyData`. The WorkStream copies the content of
2078 * the `AssemblyCopyData` object, `cell_matrix` and `cell_rhs`, into
2079 * `system_matrix` and `system_rhs`. The data member
2080 * `AssemblyCopyData::local_dof_indices` contains the indices of global
2081 * components to which cell-specific data must be copied.
2082 *
2083
2084 *
2085 * WorkStream calls the function `system_matrix_local` to compute the
2086 * cell-specific components. Likewise, WorkStream uses calls to
2087 * `copy_local_to_global` to copy the cell-specific data into the system
2088 * matrix and right-hand side. The functions `system_matrix_local` and
2089 * `copy_local_to_global` are declared last.
2090 *
2091 * @code
2092 *   using IteratorTuple =
2093 *   std::tuple<typename DoFHandler<2>::active_cell_iterator,
2095 *  
2096 *   using CellIteratorPair = SynchronousIterators<IteratorTuple>;
2097 *  
2098 *   struct AssemblyScratchData
2099 *   {
2100 *   AssemblyScratchData(const DoFHandler<2> &dof_handler,
2101 *   const DoFHandler<2> &dof_handler_rhs,
2102 *   const Vector<double> &dofs_rhs,
2103 *   const unsigned int mapping_degree,
2104 *   const UpdateFlagsCollection update_flags_collection,
2105 *   const unsigned int stage);
2106 *  
2107 *   AssemblyScratchData(const AssemblyScratchData &scratch_data);
2108 *  
2109 *   MappingQ<2> mapping;
2110 *  
2111 *   FEValues<2> fe_values;
2112 *   FEValues<2> fe_values_rhs;
2113 *  
2114 *   const unsigned int dofs_per_cell;
2115 *   const unsigned int n_q_points;
2116 *  
2117 *   std::vector<double> permeability_list;
2118 *   std::vector<double> values_list_rhs;
2119 *   std::vector<Tensor<1, 2>> vectors_list_rhs;
2120 *   std::vector<std::vector<Tensor<1, 2>>> vectors_vectors_list_rhs;
2121 *  
2122 *   const DoFHandler<2> &dof_handler_rhs;
2123 *   const Vector<double> &dofs_rhs;
2124 *   };
2125 *  
2126 *   struct AssemblyCopyData
2127 *   {
2129 *   Vector<double> cell_rhs;
2130 *   std::vector<types::global_dof_index> local_dof_indices;
2131 *   };
2132 *  
2133 *   virtual void system_matrix_local(const CellIteratorPair &IP,
2134 *   AssemblyScratchData &scratch_data,
2135 *   AssemblyCopyData &copy_data) = 0;
2136 *  
2137 *   void copy_local_to_global(const AssemblyCopyData &copy_data);
2138 *   }; // class BaseSolver
2139 *  
2140 * @endcode
2141 *
2142 * The following are the implementations of the two constructors
2143 * of the `BaseSolver` class.
2144 *
2145 * @code
2146 *   BaseSolver::BaseSolver(const unsigned int mapping_degree,
2147 *   const UpdateFlagsCollection update_flags_collection,
2148 *   const std::string &file_name,
2149 *   const Function<2> *exact_solution)
2150 *   : triangulation_rhs(triangulation)
2151 *   , dof_handler_rhs(dof_handler)
2152 *   , solution_rhs(solution)
2153 *   , exact_solution(exact_solution)
2154 *   , stage(1)
2155 *   , mapping_degree(mapping_degree)
2156 *   , update_flags_collection(update_flags_collection)
2157 *   , file_name(file_name)
2158 *   {}
2159 *  
2160 *   BaseSolver::BaseSolver(const Triangulation<2> &triangulation_rhs,
2161 *   const DoFHandler<2> &dof_handler_rhs,
2162 *   const Vector<double> &solution_rhs,
2163 *   const unsigned int stage,
2164 *   const unsigned int mapping_degree,
2165 *   const UpdateFlagsCollection update_flags_collection,
2166 *   const std::string &file_name,
2167 *   const Function<2> *exact_solution)
2168 *   : triangulation_rhs(triangulation_rhs)
2169 *   , dof_handler_rhs(dof_handler_rhs)
2170 *   , solution_rhs(solution_rhs)
2171 *   , exact_solution(exact_solution)
2172 *   , stage(stage)
2173 *   , mapping_degree(mapping_degree)
2174 *   , update_flags_collection(update_flags_collection)
2175 *   , file_name(file_name)
2176 *   {}
2177 *  
2178 * @endcode
2179 *
2180 * The following function applies the Dirichlet boundary condition, sets
2181 * up a sparsity pattern, and initializes the vectors and matrices. It is
2182 * common for the setup function to distribute the dofs. The type of the
2183 * finite elements, however, is not known at the level of `BaseSolver` class.
2184 * The type of the finite elements is chosen in the derived classes. For
2185 * this reason, the task of distributing the dofs is shifted to the end of
2186 * the `make_mesh` function which is a virtual function.
2187 *
2188
2189 *
2190 * The Dirichlet boundary condition must be enforced only in the first
2191 * [stage](@ref Step98_FourStages)
2192 * where the div-grad equation is solved for the current vector potential, T.
2193 * For this reason we have `if (stage == 1)` filter in the beginning of the
2194 * function. The boundary value problem for the curl-curl equation utilizes
2195 * the Neumann boundary condition. It is a natural boundary condition. It is
2196 * enforced by minimization of the functional. The two projectors,
2197 * @f$T \rightarrow \vec{J}_f@f$ and @f$\vec{A} \rightarrow B@f$, use no boundary
2198 * conditions.
2199 *
2200 * @code
2201 *   void BaseSolver::setup()
2202 *   {
2203 *   constraints.clear();
2204 *  
2205 *   DoFTools::make_hanging_node_constraints(dof_handler, constraints);
2206 *  
2207 *   if (stage == 1)
2209 *   dof_handler,
2210 *   Settings::outer_boundary_id,
2212 *   constraints);
2213 *  
2214 *   constraints.close();
2215 *  
2216 *   DynamicSparsityPattern dsp(dof_handler.n_dofs(), dof_handler.n_dofs());
2217 *   DoFTools::make_sparsity_pattern(dof_handler, dsp, constraints, false);
2218 *   sparsity_pattern.copy_from(dsp);
2219 *  
2220 *   system_matrix.reinit(sparsity_pattern);
2221 *   solution.reinit(dof_handler.n_dofs());
2222 *   system_rhs.reinit(dof_handler.n_dofs());
2223 *  
2224 *   if (Settings::project_exact_solution && exact_solution)
2225 *   projected_exact_solution.reinit(dof_handler.n_dofs());
2226 *  
2227 *   if (exact_solution)
2228 *   L2_per_cell.reinit(triangulation.n_active_cells());
2229 *   }
2230 *  
2231 * @endcode
2232 *
2233 * Formally, the following function assembles the system of linear equations.
2234 * In reality, however, it just spells all the magic words to get the
2235 * WorkStream going. The interesting part, i.e., computing the components of
2236 * the system matrix and the right-hand side, happens in the function
2237 * `system_matrix_local` which is a virtual function. That is to say, the
2238 * recipe for the functional is implemented in the derived class by overriding
2239 * the virtual function `system_matrix_local`.
2240 *
2241 * @code
2242 *   void BaseSolver::assemble()
2243 *   {
2244 *   WorkStream::run(CellIteratorPair({dof_handler.begin_active(),
2245 *   dof_handler_rhs.begin_active()}),
2246 *   CellIteratorPair(
2247 *   {dof_handler.end(), dof_handler_rhs.end()}),
2248 *   *this,
2249 *   &BaseSolver::system_matrix_local,
2250 *   &BaseSolver::copy_local_to_global,
2251 *   AssemblyScratchData(dof_handler,
2252 *   dof_handler_rhs,
2253 *   solution_rhs,
2254 *   mapping_degree,
2255 *   update_flags_collection,
2256 *   stage),
2257 *   AssemblyCopyData());
2258 *   }
2259 *  
2260 * @endcode
2261 *
2262 * The following are the implementation of the constructors of the
2263 * `AssemblyScratchData`. The first constructor initializes the scratch
2264 * data from the input parameters. The second - from another object of
2265 * the same type, i.e., a copy constructor.
2266 *
2267 * @code
2268 *   BaseSolver::AssemblyScratchData::AssemblyScratchData(
2269 *   const DoFHandler<2> &dof_handler,
2270 *   const DoFHandler<2> &dof_handler_rhs,
2271 *   const Vector<double> &dofs_rhs,
2272 *   const unsigned int mapping_degree,
2273 *   const UpdateFlagsCollection update_flags_collection,
2274 *   const unsigned int stage)
2275 *   : mapping(mapping_degree)
2276 *   , fe_values(mapping,
2277 *   dof_handler.get_fe(),
2278 *   QGauss<2>((stage == 1) ? (dof_handler.get_fe().degree + 1) :
2279 *   (dof_handler.get_fe().degree + 2)),
2280 *   update_flags_collection.solution_update_flags)
2281 *   , fe_values_rhs(mapping,
2282 *   dof_handler_rhs.get_fe(),
2283 *   QGauss<2>((stage == 1) ? (dof_handler.get_fe().degree + 1) :
2284 *   (dof_handler.get_fe().degree + 2)),
2285 *   update_flags_collection.rhs_update_flags)
2286 *   , dofs_per_cell(fe_values.dofs_per_cell)
2287 *   , n_q_points(fe_values.get_quadrature().size())
2288 *   , permeability_list(n_q_points)
2289 *   , values_list_rhs(n_q_points)
2290 *   , vectors_list_rhs(n_q_points)
2291 *   , vectors_vectors_list_rhs(n_q_points, std::vector<Tensor<1, 2>>(2))
2292 *   , dof_handler_rhs(dof_handler_rhs)
2293 *   , dofs_rhs(dofs_rhs)
2294 *   {}
2295 *  
2296 *   BaseSolver::AssemblyScratchData::AssemblyScratchData(
2297 *   const AssemblyScratchData &scratch_data)
2298 *   : mapping(scratch_data.mapping.get_degree())
2299 *   , fe_values(mapping,
2300 *   scratch_data.fe_values.get_fe(),
2301 *   scratch_data.fe_values.get_quadrature(),
2302 *   scratch_data.fe_values.get_update_flags())
2303 *   , fe_values_rhs(mapping,
2304 *   scratch_data.fe_values_rhs.get_fe(),
2305 *   scratch_data.fe_values_rhs.get_quadrature(),
2306 *   scratch_data.fe_values_rhs.get_update_flags())
2307 *   , dofs_per_cell(fe_values.dofs_per_cell)
2308 *   , n_q_points(fe_values.get_quadrature().size())
2309 *   , permeability_list(n_q_points)
2310 *   , values_list_rhs(n_q_points)
2311 *   , vectors_list_rhs(n_q_points)
2312 *   , vectors_vectors_list_rhs(n_q_points, std::vector<Tensor<1, 2>>(2))
2313 *   , dof_handler_rhs(scratch_data.dof_handler_rhs)
2314 *   , dofs_rhs(scratch_data.dofs_rhs)
2315 *   {}
2316 *  
2317 * @endcode
2318 *
2319 * The following function copies the components of a cell matrix and a cell
2320 * right-hand side into the system matrix, @f$A_{ij}@f$, and the system right-hand
2321 * side, @f$b_i@f$.
2322 *
2323 * @code
2324 *   void BaseSolver::copy_local_to_global(const AssemblyCopyData &copy_data)
2325 *   {
2326 *   constraints.distribute_local_to_global(copy_data.cell_matrix,
2327 *   copy_data.cell_rhs,
2328 *   copy_data.local_dof_indices,
2329 *   system_matrix,
2330 *   system_rhs);
2331 *   }
2332 *  
2333 * @endcode
2334 *
2335 * The following function solves the system of linear equations.
2336 * The stopping condition for the iteration algorithm is
2337 * @f$\|\boldsymbol{b}-\boldsymbol{A}\boldsymbol{c}\|<10^{-8}\|\boldsymbol{b}\|@f$.
2338 * The maximum number of iteration steps is set to `system_rhs.size()` as
2339 * the conjugate gradient algorithm is supposed to find the solution in at
2340 * most @f$m@f$ steps for an @f$m \times m@f$ system matrix. This function also
2341 * distributes constraints. The constraints are only used to enforce the
2342 * Dirichlet boundary condition.
2343 *
2344 * @code
2345 *   void BaseSolver::solve()
2346 *   {
2347 *   SolverControl control(system_rhs.size(), 1e-8 * system_rhs.l2_norm());
2348 *  
2350 *   SolverCG<Vector<double>> cg(control, memory);
2351 *  
2352 *   PreconditionSSOR<SparseMatrix<double>> preconditioner;
2353 *   preconditioner.initialize(system_matrix, 1.2);
2354 *  
2355 *   cg.solve(system_matrix, solution, system_rhs, preconditioner);
2356 *  
2357 *   constraints.distribute(solution);
2358 *   }
2359 *  
2360 * @endcode
2361 *
2362 * The following two functions compute the error norms and project the exact
2363 * solution.
2364 *
2365 * @code
2366 *   void BaseSolver::compute_error_norms()
2367 *   {
2368 *   if (exact_solution)
2369 *   {
2371 *   dof_handler,
2372 *   solution,
2373 *   *exact_solution,
2374 *   L2_per_cell,
2375 *   QGauss<2>(
2376 *   dof_handler.get_fe(0).degree + 4),
2378 *  
2379 *   L2_norm = VectorTools::compute_global_error(triangulation_rhs,
2380 *   L2_per_cell,
2382 *   }
2383 *   }
2384 *  
2385 *   void BaseSolver::project_exact_solution_fcn()
2386 *   {
2387 *   if (Settings::project_exact_solution && exact_solution)
2388 *   {
2389 *   AffineConstraints<double> constraints_empty;
2390 *  
2391 *   constraints_empty.clear();
2392 *  
2393 *   DoFTools::make_hanging_node_constraints(dof_handler, constraints_empty);
2394 *  
2395 *   constraints_empty.close();
2396 *  
2397 *   VectorTools::project(MappingQ<2>(mapping_degree),
2398 *   dof_handler,
2399 *   constraints_empty,
2400 *   QGauss<2>((stage == 1) ?
2401 *   (dof_handler.get_fe().degree + 1) :
2402 *   (dof_handler.get_fe().degree + 2)),
2403 *   *exact_solution,
2404 *   projected_exact_solution);
2405 *   }
2406 *   }
2407 *  
2408 * @endcode
2409 *
2410 * The following function saves the solution and the @f$L_2@f$ error norm into a
2411 * `.vtu` file. If `Settings::project_exact_solution = true`, the projected
2412 * exact solution is saved as well.
2413 *
2414 * @code
2415 *   void BaseSolver::output_results() const
2416 *   {
2417 *   const bool fe_is_vector = ((stage == 2) || (stage == 3));
2418 *  
2419 *   const std::string name = (fe_is_vector ? "VectorField" : "ScalarField");
2420 *   const unsigned int components = (fe_is_vector ? 2 : 1);
2422 *   component_interpretation =
2423 *   (fe_is_vector ?
2426 *  
2427 *   std::vector<std::string> solution_names(components, name);
2428 *   std::vector<DataComponentInterpretation::DataComponentInterpretation>
2429 *   data_component_interpretation(components, component_interpretation);
2430 *  
2431 *   DataOut<2> data_out;
2432 *  
2433 *   data_out.add_data_vector(dof_handler,
2434 *   solution,
2435 *   solution_names,
2436 *   data_component_interpretation);
2437 *  
2438 *   if (exact_solution)
2439 *   {
2440 *   data_out.add_data_vector(L2_per_cell, "L2norm");
2441 *  
2442 *   if (Settings::project_exact_solution)
2443 *   {
2444 *   std::vector<std::string> solution_names_exact(components,
2445 *   name + "Exact");
2446 *   data_out.add_data_vector(dof_handler,
2447 *   projected_exact_solution,
2448 *   solution_names_exact,
2449 *   data_component_interpretation);
2450 *   }
2451 *   }
2452 *  
2453 *   DataOutBase::VtkFlags flags;
2454 *   flags.write_higher_order_cells = true;
2455 *   data_out.set_flags(flags);
2456 *  
2457 *   const MappingQ<2> mapping(mapping_degree);
2458 *  
2459 *   data_out.build_patches(mapping,
2460 *   dof_handler.get_fe(0).degree + 2,
2462 *  
2463 *   std::ofstream ofs(file_name + ".vtu");
2464 *   data_out.write_vtu(ofs);
2465 *   }
2466 *  
2467 * @endcode
2468 *
2469 * The following function clears the memory for the next solver.
2470 *
2471 * @code
2472 *   void BaseSolver::clear()
2473 *   {
2474 *   system_matrix.clear();
2475 *   system_rhs.reinit(0);
2476 *   }
2477 *  
2478 * @endcode
2479 *
2480 * The following functions calls all the constituent functions in the right
2481 * order.
2482 *
2483 * @code
2484 *   void BaseSolver::run()
2485 *   {
2486 *   make_mesh();
2487 *   setup();
2488 *   assemble();
2489 *   solve();
2490 *   compute_error_norms();
2491 *   project_exact_solution_fcn();
2492 *   output_results();
2493 *   clear();
2494 *   }
2495 *  
2496 * @endcode
2497 *
2498 * The following is the straightforward implementation of the get functions.
2499 *
2500 * @code
2501 *   double BaseSolver::get_L2_norm() const
2502 *   {
2503 *   return L2_norm;
2504 *   }
2505 *  
2506 *   unsigned int BaseSolver::get_n_cells() const
2507 *   {
2508 *   return triangulation_rhs.n_active_cells();
2509 *   }
2510 *  
2511 *   types::global_dof_index BaseSolver::get_n_dofs() const
2512 *   {
2513 *   return dof_handler.n_dofs();
2514 *   }
2515 *  
2516 *   const Triangulation<2> &BaseSolver::get_tria() const
2517 *   {
2518 *   return triangulation_rhs;
2519 *   }
2520 *  
2521 *   const DoFHandler<2> &BaseSolver::get_dof_handler() const
2522 *   {
2523 *   return dof_handler;
2524 *   }
2525 *  
2526 *   const Vector<double> &BaseSolver::get_solution() const
2527 *   {
2528 *   return solution;
2529 *   }
2530 *   } // namespace BaseClasses
2531 *  
2532 * @endcode
2533 *
2534 *
2535 * <a name="step_98-SolverT"></a>
2536 * <h3>Solver - T</h3>
2537 *
2538
2539 *
2540 * The following namespace contains the code related to the computation of the
2541 * current vector potential, @f$T@f$.
2542 *
2543 * @code
2544 *   namespace SolverT
2545 *   {
2546 *   using namespace BaseClasses;
2547 *  
2548 * @endcode
2549 *
2550 * We derive the solver from the `BaseSolver` class. What is left to do is
2551 * to initialize the `BaseSolver`, override the two virtual functions
2552 * (`make_mesh` and `system_matrix_local`), and implement the function
2553 * `free_current_density`. The function `free_current_density` implements
2554 * the closed-form analytical expression for @f$\vec{J}_f@f$ on the right-hand
2555 * side of the [div-grad equation](@ref Step98_FourStages).
2556 *
2557 * @code
2558 *   class Solver : public BaseSolver
2559 *   {
2560 *   public:
2561 *   Solver() = delete;
2562 *   Solver(const unsigned int p, // Degree of the FE_Q finite elements.
2563 *   const unsigned int r, // The mesh refinement parameter.
2564 *   const unsigned int mapping_degree,
2565 *   const std::string &file_name = "data",
2566 *   const Function<2> *exact_solution = nullptr);
2567 *  
2568 *   private:
2569 *   virtual void make_mesh() override final;
2570 *   virtual void
2571 *   system_matrix_local(const CellIteratorPair &IP,
2572 *   AssemblyScratchData &scratch_data,
2573 *   AssemblyCopyData &copy_data) override final;
2574 *  
2575 *   const FE_Q<2> fe;
2576 *   const unsigned int refinement_parameter;
2577 *  
2578 *   void free_current_density(const std::vector<Point<2>> &p,
2579 *   const types::material_id material_id,
2580 *   std::vector<Tensor<1, 2>> &values) const;
2581 *   };
2582 *  
2583 * @endcode
2584 *
2585 * Following is the implementation of the constructor.
2586 * We use the first constructor of the `BaseSolver` class as
2587 * the solver is used at the
2588 * [first stage](@ref Step98_FourStages). By looking at the
2589 * [expressions](@ref Step98_Numerical_Recipe_T)
2590 * for @f$A_{ij}@f$ and @f$b_i@f$ we can conclude that to compute them we need
2591 * gradients of the shape functions, quadrature points, and the quadrature
2592 * weights multiplied by the Jacobian determinant(`JxW`). The quadrature
2593 * points are needed to sample the closed-form analytical expression for
2594 * @f$\vec{J}_f@f$. Accordingly, we use the update flags
2596 * for the FE_Q finite elements. This time we do not use the finite elements
2597 * that model the potential on the right-hand side of the equation, so we
2598 * set `update_default` for the right-hand side finite elements.
2599 *
2600 * @code
2601 *   Solver::Solver(const unsigned int p,
2602 *   const unsigned int r,
2603 *   const unsigned int mapping_degree,
2604 *   const std::string &file_name,
2605 *   const Function<2> *exact_solution)
2606 *   : BaseSolver(mapping_degree,
2609 *   update_default},
2610 *   file_name,
2611 *   exact_solution)
2612 *  
2613 *   , fe(p)
2614 *   , refinement_parameter(r)
2615 *   {}
2616 *  
2617 * @endcode
2618 *
2619 * The following function loads the mesh, creates manifolds, bounds the
2620 * manifolds to the manifold IDs, refines the mesh, and distributes the dofs.
2621 * Note that we are allowed to create the manifolds locally in the function as
2622 * the triangulation object keeps copies of the manifolds, see @ref step_65 "step-65". Also
2623 * recall that we have shifted the task of distributing the dofs from the
2624 * `setup` function to the `make_mesh` function to evade the necessity to make
2625 * the `setup` function virtual. This allows us to keep one `setup` function
2626 * in the `BaseSolver` class that serves the needs of all derived classes.
2627 *
2628 * @code
2629 *   void Solver::make_mesh()
2630 *   {
2631 *   GridIn<2> gridin;
2632 *  
2633 *   gridin.attach_triangulation(triangulation);
2634 *   gridin.read_msh("circle.msh");
2635 *  
2636 *   using namespace Settings;
2637 *  
2638 *   triangulation.set_manifold(spherical_manifold_id, SphericalManifold<2>());
2639 *   triangulation.set_manifold(flat_manifold_id, FlatManifold<2>());
2640 *  
2641 *   TransfiniteInterpolationManifold<2> transfinite_manifold;
2642 *   transfinite_manifold.initialize(triangulation);
2643 *   triangulation.set_manifold(transfinite_interpolation_manifold_id,
2644 *   transfinite_manifold);
2645 *  
2646 *   triangulation.refine_global(refinement_parameter);
2647 *  
2648 *   dof_handler.reinit(triangulation);
2649 *   dof_handler.distribute_dofs(fe);
2650 *   }
2651 *  
2652 * @endcode
2653 *
2654 * The following function assembles a fraction of
2655 * [the system matrix and the system right-hand side](@ref Step98_Numerical_Recipe_T)
2656 * related to a single cell. These fractions are
2657 * `copy_data.cell_matrix` and `copy_data.cell_rhs`. They are copied to
2658 * `system_matrix` and `system_rhs` by WorkStream.
2659 *
2660 * @code
2661 *   void Solver::system_matrix_local(const CellIteratorPair &IP,
2662 *   AssemblyScratchData &scratch_data,
2663 *   AssemblyCopyData &copy_data)
2664 *   {
2665 *   const FEValuesExtractors::Scalar se(0);
2666 *  
2667 *   copy_data.cell_matrix.reinit(scratch_data.dofs_per_cell,
2668 *   scratch_data.dofs_per_cell);
2669 *  
2670 *   copy_data.cell_rhs.reinit(scratch_data.dofs_per_cell);
2671 *  
2672 *   copy_data.local_dof_indices.resize(scratch_data.dofs_per_cell);
2673 *  
2674 *   const typename DoFHandler<2>::active_cell_iterator cell = std::get<0>(*IP);
2675 *  
2676 *   scratch_data.fe_values.reinit(cell);
2677 *  
2678 *   Solver::free_current_density(scratch_data.fe_values.get_quadrature_points(),
2679 *   cell->material_id(),
2680 *   scratch_data.vectors_list_rhs);
2681 *  
2682 *   for (unsigned int q_index = 0; q_index < scratch_data.n_q_points; ++q_index)
2683 *   {
2684 *   for (unsigned int i = 0; i < scratch_data.dofs_per_cell; ++i)
2685 *   {
2686 *   for (unsigned int j = 0; j < scratch_data.dofs_per_cell; ++j)
2687 *   {
2688 *   copy_data.cell_matrix(i, j) += // Integral I_a1.
2689 *   (scratch_data.fe_values[se].gradient(
2690 *   i, q_index) * // grad phi_i(x_q)
2691 *   scratch_data.fe_values[se].gradient(
2692 *   j, q_index) // grad phi_j(x_q)
2693 *   ) *
2694 *   scratch_data.fe_values.JxW(q_index); // dx
2695 *   }
2696 *   copy_data.cell_rhs(i) += // Integral I_b3-1.
2697 *   (scratch_data.vectors_list_rhs[q_index][0] *
2698 *   scratch_data.fe_values[se].gradient(i, q_index)[1] -
2699 *   scratch_data.vectors_list_rhs[q_index][1] *
2700 *   scratch_data.fe_values[se].gradient(i,
2701 *   q_index)[0]) *
2702 *   scratch_data.fe_values.JxW(
2703 *   q_index); // J_f(x_q).(curlv phi_i(x_q))dx.
2704 *   }
2705 *   }
2706 *  
2707 *   cell->get_dof_indices(copy_data.local_dof_indices);
2708 *   }
2709 *  
2710 * @endcode
2711 *
2712 * The following function implements the closed form analytical
2713 * [expression](@ref Step98_Equation_1_Jf)
2714 * for @f$\vec{J}_f@f$ on the right-hand side of the
2715 * [div-grad equation](@ref Step98_PDE_T).
2716 *
2717 * @code
2718 *   void Solver::free_current_density(const std::vector<Point<2>> &p,
2719 *   const types::material_id material_id,
2720 *   std::vector<Tensor<1, 2>> &values) const
2721 *   {
2722 *   Assert(p.size() == values.size(),
2723 *   ExcDimensionMismatch(p.size(), values.size()));
2724 *  
2725 *   if ((material_id == Settings::material_id_free_space) ||
2726 *   (material_id == Settings::material_id_core))
2727 *   std::fill(values.begin(), values.end(), Tensor<1, 2>());
2728 *  
2729 *   if (material_id == Settings::material_id_free_current)
2730 *   for (unsigned int i = 0; i < values.size(); i++)
2731 *   values[i] =
2732 *   ExactSolutions::volume_free_current_density(p[i], Settings::K0);
2733 *   }
2734 *   } // namespace SolverT
2735 *  
2736 * @endcode
2737 *
2738 *
2739 * <a name="step_98-ProjectorfromHgradtoHdiv"></a>
2740 * <h3>Projector from H(grad) to H(div)</h3>
2741 *
2742
2743 *
2744 * The following namespace contains all the code related to the computation of
2745 * the free-current density, @f$\vec{J}_f@f$.
2746 *
2747 * @code
2748 *   namespace ProjectorHgradToHdiv
2749 *   {
2750 *   using namespace BaseClasses;
2751 *  
2752 * @endcode
2753 *
2754 * We derive the solver from the `BaseSolver` class. What is left to do is
2755 * to initialize the `BaseSolver` and override the two virtual functions
2756 * (`make_mesh` and `system_matrix_local`).
2757 *
2758 * @code
2759 *   class Solver : public BaseSolver
2760 *   {
2761 *   public:
2762 *   Solver() = delete;
2763 *   Solver(const unsigned int p, /* Degree of the FE_RaviartThomas finite
2764 *   elements. */
2765 *   const unsigned int mapping_degree,
2766 *   const Triangulation<2> &triangulation_rhs,
2767 *   const DoFHandler<2> &dof_handler_rhs,
2768 *   const Vector<double> &solution_rhs,
2769 *   const std::string &file_name = "data",
2770 *   const Function<2> *exact_solution = nullptr);
2771 *  
2772 *   private:
2773 *   virtual void make_mesh() override final;
2774 *   virtual void
2775 *   system_matrix_local(const CellIteratorPair &IP,
2776 *   AssemblyScratchData &scratch_data,
2777 *   AssemblyCopyData &copy_data) override final;
2778 *  
2779 *   FE_RaviartThomas<2> fe;
2780 *   };
2781 *  
2782 * @endcode
2783 *
2784 * Following is the implementation of the constructor.
2785 * We use the second constructor of the `BaseSolver` class as
2786 * the solver is used at the
2787 * [second stage](@ref Step98_FourStages). By looking at the
2788 * [expressions](@ref Step98_Numerical_Recipe_Jf)
2789 * for @f$A_{ij}@f$ and @f$b_i@f$ we can conclude that to compute them we need
2790 * values of the shape functions and the quadrature weights multiplied by the
2791 * Jacobian determinant(`JxW`) from the FE_RaviartThomas finite elements.
2792 * Accordingly, we use the update flags `update_values`, and
2793 * `update_JxW_values` for the FE_RaviartThomas finite elements. This time
2794 * there is a numerically computed potential, @f$T@f$, on the right-hand side of
2795 * the [equation](@ref Step98_PDE_Jf). It is modeled by the FE_Q finite elements.
2796 * To compute the right-hand side, we need gradients of the shape functions.
2797 * Accordingly, we use the update flag `update_gradients` for the FE_Q finite
2798 * elements.
2799 *
2800 * @code
2801 *   Solver::Solver(const unsigned int p,
2802 *   const unsigned int mapping_degree,
2803 *   const Triangulation<2> &triangulation_rhs,
2804 *   const DoFHandler<2> &dof_handler_rhs,
2805 *   const Vector<double> &solution_rhs,
2806 *   const std::string &file_name,
2807 *   const Function<2> *exact_solution)
2808 *   : BaseSolver(triangulation_rhs,
2809 *   dof_handler_rhs,
2810 *   solution_rhs,
2811 *   2,
2812 *   mapping_degree,
2814 *   file_name,
2815 *   exact_solution)
2816 *   , fe(p)
2817 *   {}
2818 *  
2819 * @endcode
2820 *
2821 * At the second stage we do not load the mesh. We reuse the mesh loaded
2822 * at the first stage. Consequently, we just need to distribute the dofs.
2823 *
2824 * @code
2825 *   void Solver::make_mesh()
2826 *   {
2827 *   dof_handler.reinit(triangulation_rhs);
2828 *   dof_handler.distribute_dofs(fe);
2829 *   }
2830 *  
2831 * @endcode
2832 *
2833 * The following function assembles a fraction of
2834 * [the system matrix and the system right-hand side](@ref Step98_Numerical_Recipe_Jf)
2835 * related to a single cell. These fractions are
2836 * `copy_data.cell_matrix` and `copy_data.cell_rhs`. They are copied to
2837 * `system_matrix` and `system_rhs` by WorkStream.
2838 *
2839 * @code
2840 *   void Solver::system_matrix_local(const CellIteratorPair &IP,
2841 *   AssemblyScratchData &scratch_data,
2842 *   AssemblyCopyData &copy_data)
2843 *   {
2844 *   const FEValuesExtractors::Vector ve(0);
2845 *  
2846 *   copy_data.cell_matrix.reinit(scratch_data.dofs_per_cell,
2847 *   scratch_data.dofs_per_cell);
2848 *  
2849 *   copy_data.cell_rhs.reinit(scratch_data.dofs_per_cell);
2850 *  
2851 *   copy_data.local_dof_indices.resize(scratch_data.dofs_per_cell);
2852 *  
2853 *   const typename DoFHandler<2>::active_cell_iterator cell = std::get<0>(*IP);
2854 *   const typename DoFHandler<2>::active_cell_iterator cell_rhs =
2855 *   std::get<1>(*IP);
2856 *  
2857 *   scratch_data.fe_values.reinit(cell);
2858 *   scratch_data.fe_values_rhs.reinit(cell_rhs);
2859 *  
2860 *   scratch_data.fe_values_rhs.get_function_gradients(
2861 *   scratch_data.dofs_rhs, scratch_data.vectors_list_rhs);
2862 *  
2863 *   for (unsigned int q_index = 0; q_index < scratch_data.n_q_points; ++q_index)
2864 *   {
2865 *   for (unsigned int i = 0; i < scratch_data.dofs_per_cell; ++i)
2866 *   {
2867 *   for (unsigned int j = 0; j < scratch_data.dofs_per_cell; ++j)
2868 *   {
2869 *   copy_data.cell_matrix(i, j) += // Integral I_a
2870 *   scratch_data.fe_values[ve].value(i, q_index) * // phi_i(x_q)
2871 *   scratch_data.fe_values[ve].value(j,
2872 *   q_index) * // phi_j(x_q)
2873 *   scratch_data.fe_values.JxW(q_index); // dx
2874 *   }
2875 *  
2876 *   copy_data.cell_rhs(i) += // Integral I_b
2877 *   (scratch_data.vectors_list_rhs[q_index][1] *
2878 *   scratch_data.fe_values[ve].value(i, q_index)[0] -
2879 *   scratch_data.vectors_list_rhs[q_index][0] *
2880 *   scratch_data.fe_values[ve].value(i, q_index)[1]) *
2881 *   scratch_data.fe_values.JxW(
2882 *   q_index); // [curlv T(x_q)].phi_i(x_q)dx
2883 *   }
2884 *   }
2885 *  
2886 *   cell->get_dof_indices(copy_data.local_dof_indices);
2887 *   }
2888 *   } // namespace ProjectorHgradToHdiv
2889 *  
2890 *  
2891 *  
2892 * @endcode
2893 *
2894 *
2895 * <a name="step_98-SolverA"></a>
2896 * <h3>Solver - A</h3>
2897 *
2898
2899 *
2900 * The following namespace contains all the code related to the computation of
2901 * the magnetic vector potential, @f$\vec{A}@f$.
2902 *
2903 * @code
2904 *   namespace SolverA
2905 *   {
2906 *   using namespace BaseClasses;
2907 *  
2908 * @endcode
2909 *
2910 * We derive the solver from the `BaseSolver` class. What is left to do is
2911 * to initialize the `BaseSolver`, override the two virtual functions
2912 * (`make_mesh` and `system_matrix_local`), and implement the function
2913 * `permeability`.
2914 *
2915 * @code
2916 *   class Solver : public BaseSolver
2917 *   {
2918 *   public:
2919 *   Solver() = delete;
2920 *   Solver(const unsigned int p, // Degree of the FE_Nedelec finite elements.
2921 *   const unsigned int mapping_degree,
2922 *   const Triangulation<2> &triangulation_ext,
2923 *   const DoFHandler<2> &dof_handler_ext,
2924 *   const Vector<double> &solution_ext,
2925 *   const std::string &file_name = "data",
2926 *   const Function<2> *exact_solution = nullptr);
2927 *  
2928 *   private:
2929 *   virtual void make_mesh() override final;
2930 *   virtual void
2931 *   system_matrix_local(const CellIteratorPair &IP,
2932 *   AssemblyScratchData &scratch_data,
2933 *   AssemblyCopyData &copy_data) override final;
2934 *  
2935 *   FE_Nedelec<2> fe;
2936 *  
2937 *   void permeability(const types::material_id material_id,
2938 *   std::vector<double> &values) const;
2939 *   };
2940 *  
2941 * @endcode
2942 *
2943 * Following is the implementation of the constructor.
2944 * We use the second constructor of the `BaseSolver` class as
2945 * the solver is used at the
2946 * [third stage](@ref Step98_FourStages). By looking at the
2947 * [expressions](@ref Step98_Numerical_Recipe_A)
2948 * for @f$A_{ij}@f$ and @f$b_i@f$ we can conclude that to compute them we need
2949 * values of the shape functions, their gradients, and the quadrature
2950 * weights multiplied by the Jacobian determinant(`JxW`) from the FE_Nedelec
2951 * finite elements. Accordingly, we use the update flags `update_values`,
2952 * `update_gradients`, and `update_JxW_values` for the FE_Nedelec finite
2953 * elements. This time there is a numerically computed potential, @f$T@f$, on
2954 * the right-hand side of the
2955 * [equation](@ref Step98_PDE_A). It is modeled by the FE_Q finite elements.
2956 * To compute it, we need values of the shape functions. Accordingly, we use
2957 * the update flag `update_values` for the FE_Q finite elements.
2958 *
2959 * @code
2960 *   Solver::Solver(const unsigned int p,
2961 *   const unsigned int mapping_degree,
2962 *   const Triangulation<2> &triangulation_rhs,
2963 *   const DoFHandler<2> &dof_handler_rhs,
2964 *   const Vector<double> &solution_rhs,
2965 *   const std::string &file_name,
2966 *   const Function<2> *exact_solution)
2967 *   : BaseSolver(triangulation_rhs,
2968 *   dof_handler_rhs,
2969 *   solution_rhs,
2970 *   3,
2971 *   mapping_degree,
2973 *   update_values},
2974 *   file_name,
2975 *   exact_solution)
2976 *   , fe(p)
2977 *   {}
2978 *  
2979 * @endcode
2980 *
2981 * At the third stage we do not load the mesh. We reuse the mesh loaded at
2982 * the first stage. Consequently, we just need to distribute the dofs.
2983 *
2984 * @code
2985 *   void Solver::make_mesh()
2986 *   {
2987 *   dof_handler.reinit(triangulation_rhs);
2988 *   dof_handler.distribute_dofs(fe);
2989 *   }
2990 *  
2991 * @endcode
2992 *
2993 * The following function assembles a fraction of
2994 * [the system matrix and the system right-hand side](@ref Step98_Numerical_Recipe_A)
2995 * related to a single cell. These fractions are
2996 * `copy_data.cell_matrix` and `copy_data.cell_rhs`. They are copied to
2997 * `system_matrix` and `system_rhs` by WorkStream.
2998 *
2999 * @code
3000 *   void Solver::system_matrix_local(const CellIteratorPair &IP,
3001 *   AssemblyScratchData &scratch_data,
3002 *   AssemblyCopyData &copy_data)
3003 *   {
3004 *   const FEValuesExtractors::Vector ve(0);
3005 *  
3006 *   copy_data.cell_matrix.reinit(scratch_data.dofs_per_cell,
3007 *   scratch_data.dofs_per_cell);
3008 *  
3009 *   copy_data.cell_rhs.reinit(scratch_data.dofs_per_cell);
3010 *  
3011 *   copy_data.local_dof_indices.resize(scratch_data.dofs_per_cell);
3012 *  
3013 *   const typename DoFHandler<2>::active_cell_iterator cell = std::get<0>(*IP);
3014 *   const typename DoFHandler<2>::active_cell_iterator cell_rhs =
3015 *   std::get<1>(*IP);
3016 *  
3017 *   scratch_data.fe_values.reinit(cell);
3018 *   scratch_data.fe_values_rhs.reinit(cell_rhs);
3019 *  
3020 *   Solver::permeability(cell->material_id(), scratch_data.permeability_list);
3021 *  
3022 *   scratch_data.fe_values_rhs.get_function_values(
3023 *   scratch_data.dofs_rhs, scratch_data.values_list_rhs);
3024 *  
3025 *   for (unsigned int q_index = 0; q_index < scratch_data.n_q_points; ++q_index)
3026 *   {
3027 *   for (unsigned int i = 0; i < scratch_data.dofs_per_cell; ++i)
3028 *   {
3029 *   for (unsigned int j = 0; j < scratch_data.dofs_per_cell; ++j)
3030 *   {
3031 *   copy_data.cell_matrix(i, j) += // Integral I_a1+I_a3.
3032 *   (1.0 / scratch_data.permeability_list[q_index]) * // 1 / mu
3033 *   (scratch_data.fe_values[ve].curl(
3034 *   i, q_index) * // curls phi_i(x_q)
3035 *   scratch_data.fe_values[ve].curl(
3036 *   j, q_index) // curls phi_j(x_q)
3037 *   +
3038 *   Settings::eta_squared * // eta^2
3039 *   scratch_data.fe_values[ve].value(i,
3040 *   q_index) * // phi_i(x_q)
3041 *   scratch_data.fe_values[ve].value(j, q_index) // phi_j(x_q)
3042 *   ) *
3043 *   scratch_data.fe_values.JxW(q_index); // dx
3044 *   }
3045 *   copy_data.cell_rhs(i) += // Integral I_b3-1.
3046 *   (scratch_data.values_list_rhs[q_index] *
3047 *   scratch_data.fe_values[ve].curl(i, q_index)) *
3048 *   scratch_data.fe_values.JxW(q_index); // T(x_q)(curls phi_i(x_q))dx
3049 *   }
3050 *   }
3051 *  
3052 *   cell->get_dof_indices(copy_data.local_dof_indices);
3053 *   }
3054 *  
3055 * @endcode
3056 *
3057 * The following function implements the
3058 * [equation](@ref Step98_Equation_MU)
3059 * for permeability.
3060 *
3061 * @code
3062 *   void Solver::permeability(const types::material_id material_id,
3063 *   std::vector<double> &values) const
3064 *   {
3065 *   if (material_id == Settings::material_id_core)
3066 *   std::fill(values.begin(), values.end(), Settings::mu_1);
3067 *   else
3068 *   std::fill(values.begin(), values.end(), Settings::mu_0);
3069 *   }
3070 *   } // namespace SolverA
3071 *  
3072 * @endcode
3073 *
3074 *
3075 * <a name="step_98-ProjectorfromHcurltoL2"></a>
3076 * <h3>Projector from H(curl) to L2</h3>
3077 *
3078
3079 *
3080 * The following namespace contains all the code related to the computation of
3081 * the magnetic field, @f$B@f$.
3082 *
3083 * @code
3084 *   namespace ProjectorHcurlToL2
3085 *   {
3086 *   using namespace BaseClasses;
3087 *  
3088 * @endcode
3089 *
3090 * We derive the solver from the `BaseSolver` class. What is left to do is
3091 * to initialize the `BaseSolver` and override the two virtual functions
3092 * (`make_mesh` and `system_matrix_local`).
3093 *
3094 * @code
3095 *   class Solver : public BaseSolver
3096 *   {
3097 *   public:
3098 *   Solver() = delete;
3099 *   Solver(const unsigned int p, // Degree of the FE_DGQ finite elements.
3100 *   const unsigned int mapping_degree,
3101 *   const Triangulation<2> &triangulation_rhs,
3102 *   const DoFHandler<2> &dof_handler_rhs,
3103 *   const Vector<double> &solution_rhs,
3104 *   const std::string &file_name = "data",
3105 *   const Function<2> *exact_solution = nullptr);
3106 *  
3107 *   private:
3108 *   virtual void make_mesh() override final;
3109 *   virtual void
3110 *   system_matrix_local(const CellIteratorPair &IP,
3111 *   AssemblyScratchData &scratch_data,
3112 *   AssemblyCopyData &copy_data) override final;
3113 *  
3114 *   FE_DGQ<2> fe;
3115 *   };
3116 *  
3117 * @endcode
3118 *
3119 * Following is the implementation of the constructor.
3120 * We use the second constructor of the `BaseSolver` class as
3121 * the solver is used at the
3122 * [fourth stage](@ref Step98_FourStages). By looking at the
3123 * [expressions](@ref Step98_Numerical_Recipe_B)
3124 * for @f$A_{ij}@f$ and @f$b_i@f$ we can conclude that to compute them we need
3125 * values of the shape functions and the quadrature weights multiplied by the
3126 * Jacobian determinant(`JxW`) from the FE_DGQ finite elements.
3127 * Accordingly, we use the update flags `update_values`, and
3128 * `update_JxW_values` for the FE_DGQ finite elements. This time there is a
3129 * numerically computed potential, @f$\vec{A}@f$, on the right-hand side of the
3130 * [equation](@ref Step98_PDE_B). It is modeled by the FE_Nedelec finite
3131 * elements. To compute the right-hand side, we need gradients of the shape
3132 * functions. Accordingly, we use the update flag `update_gradients` for the
3133 * FE_Nedelec finite elements.
3134 *
3135 * @code
3136 *   Solver::Solver(const unsigned int p,
3137 *   const unsigned int mapping_degree,
3138 *   const Triangulation<2> &triangulation_ext,
3139 *   const DoFHandler<2> &dof_handler_ext,
3140 *   const Vector<double> &solution_ext,
3141 *   const std::string &file_name,
3142 *   const Function<2> *exact_solution)
3143 *   : BaseSolver(triangulation_ext,
3144 *   dof_handler_ext,
3145 *   solution_ext,
3146 *   4,
3147 *   mapping_degree,
3149 *   file_name,
3150 *   exact_solution)
3151 *   , fe(p)
3152 *   {}
3153 *  
3154 * @endcode
3155 *
3156 * At the fourth stage we do not load the mesh. We reuse the mesh loaded
3157 * at the first stage. Consequently, we just need to distribute the dofs.
3158 *
3159 * @code
3160 *   void Solver::make_mesh()
3161 *   {
3162 *   dof_handler.reinit(triangulation_rhs);
3163 *   dof_handler.distribute_dofs(fe);
3164 *   }
3165 *  
3166 * @endcode
3167 *
3168 * The following function assembles a fraction of
3169 * [the system matrix and the system right-hand side](@ref Step98_Numerical_Recipe_B)
3170 * related to a single cell. These fractions are
3171 * `copy_data.cell_matrix` and `copy_data.cell_rhs`. They are copied to
3172 * `system_matrix` and `system_rhs` by WorkStream.
3173 *
3174 * @code
3175 *   void Solver::system_matrix_local(const CellIteratorPair &IP,
3176 *   AssemblyScratchData &scratch_data,
3177 *   AssemblyCopyData &copy_data)
3178 *   {
3179 *   const FEValuesExtractors::Scalar se(0);
3180 *  
3181 *   copy_data.cell_matrix.reinit(scratch_data.dofs_per_cell,
3182 *  
3183 *   scratch_data.dofs_per_cell);
3184 *  
3185 *   copy_data.cell_rhs.reinit(scratch_data.dofs_per_cell);
3186 *  
3187 *   copy_data.local_dof_indices.resize(scratch_data.dofs_per_cell);
3188 *  
3189 *   const typename DoFHandler<2>::active_cell_iterator cell = std::get<0>(*IP);
3190 *   const typename DoFHandler<2>::active_cell_iterator cell_rhs =
3191 *   std::get<1>(*IP);
3192 *  
3193 *   scratch_data.fe_values.reinit(cell);
3194 *   scratch_data.fe_values_rhs.reinit(cell_rhs);
3195 *  
3196 *   scratch_data.fe_values_rhs.get_function_gradients(
3197 *   scratch_data.dofs_rhs, scratch_data.vectors_vectors_list_rhs);
3198 *  
3199 *   for (unsigned int q_index = 0; q_index < scratch_data.n_q_points; ++q_index)
3200 *   {
3201 *   for (unsigned int i = 0; i < scratch_data.dofs_per_cell; ++i)
3202 *   {
3203 *   for (unsigned int j = 0; j < scratch_data.dofs_per_cell; ++j)
3204 *   {
3205 *   copy_data.cell_matrix(i, j) += // Integral I_a
3206 *   scratch_data.fe_values[se].value(i, q_index) * // phi_i(x_q)
3207 *   scratch_data.fe_values[se].value(j, q_index) * // phi_j(x_q)
3208 *   scratch_data.fe_values.JxW(q_index); // dx
3209 *   }
3210 *  
3211 *   copy_data.cell_rhs(i) += // Integral I_b
3212 *   (scratch_data.vectors_vectors_list_rhs[q_index][1][0] -
3213 *   scratch_data.vectors_vectors_list_rhs[q_index][0][1]) *
3214 *   scratch_data.fe_values[se].value(i, q_index) *
3215 *   scratch_data.fe_values.JxW(
3216 *   q_index); // (curls A(x_q)) phi_i(x_q) dx
3217 *   }
3218 *   }
3219 *  
3220 *   cell->get_dof_indices(copy_data.local_dof_indices);
3221 *   }
3222 *   } // namespace ProjectorHcurlToL2
3223 *  
3224 * @endcode
3225 *
3226 *
3227 * <a name="step_98-Themainloop"></a>
3228 * <h3>The main loop</h3>
3229 *
3230
3231 *
3232 * The `MagneticProblem` class hosts the main loop inside the `run`
3233 * function. The implementation of the loop is straightforward -
3234 * we create and run the four solvers one-by-one and copy the relevant
3235 * data into convergence tables.
3236 *
3237 * @code
3238 *   class MagneticProblem
3239 *   {
3240 *   public:
3241 *   void run()
3242 *   {
3243 *   if (Settings::n_threads_max != 0)
3244 *   MultithreadInfo::set_thread_limit(Settings::n_threads_max);
3245 *  
3246 *   MainOutputTable table_T(2);
3247 *   MainOutputTable table_Jf(2);
3248 *   MainOutputTable table_A(2);
3249 *   MainOutputTable table_B(2);
3250 *  
3251 *   std::cout << "Solving for (p' = " << Settings::fe_degree + 1
3252 *   << "; p = " << Settings::fe_degree << "): " << std::flush;
3253 *  
3254 *   for (unsigned int r = 1; r < 5; r++) // Mesh refinement parameter.
3255 *   {
3256 *   table_T.add_value("r", r);
3257 *   table_T.add_value("p", Settings::fe_degree + 1);
3258 *  
3259 *   table_Jf.add_value("r", r);
3260 *   table_Jf.add_value("p", Settings::fe_degree);
3261 *  
3262 *   table_A.add_value("r", r);
3263 *   table_A.add_value("p", Settings::fe_degree);
3264 *  
3265 *   table_B.add_value("r", r);
3266 *   table_B.add_value("p", Settings::fe_degree);
3267 *  
3268 * @endcode
3269 *
3270 * Stage 1. Computing @f$T@f$.
3271 *
3272
3273 *
3274 *
3275 * @code
3276 *   std::cout << "T " << std::flush;
3277 *  
3278 *   ExactSolutions::CurrentVectorPotential T_exact;
3279 *  
3280 *   SolverT::Solver T(Settings::fe_degree + 1,
3281 *   r,
3282 *   Settings::mapping_degree,
3283 *   "T_p" + std::to_string(Settings::fe_degree + 1) +
3284 *   "_r" + std::to_string(r),
3285 *   &T_exact);
3286 *  
3287 *   T.run();
3288 *  
3289 *   table_T.add_value("ndofs", T.get_n_dofs());
3290 *   table_T.add_value("ncells", T.get_n_cells());
3291 *   table_T.add_value("L2", T.get_L2_norm());
3292 *  
3293 * @endcode
3294 *
3295 * Stage 2. Computing @f$\vec{J}_f@f$.
3296 *
3297
3298 *
3299 *
3300 * @code
3301 *   std::cout << "Jf " << std::flush;
3302 *  
3303 *   ExactSolutions::FreeCurrentDensity Jf_exact;
3304 *  
3305 *   ProjectorHgradToHdiv::Solver Jf(Settings::fe_degree,
3306 *   Settings::mapping_degree,
3307 *   T.get_tria(),
3308 *   T.get_dof_handler(),
3309 *   T.get_solution(),
3310 *   "Jf_p" +
3311 *   std::to_string(Settings::fe_degree) +
3312 *   "_r" + std::to_string(r),
3313 *   &Jf_exact);
3314 *  
3315 *   Jf.run();
3316 *  
3317 *   table_Jf.add_value("ndofs", Jf.get_n_dofs());
3318 *   table_Jf.add_value("ncells", Jf.get_n_cells());
3319 *   table_Jf.add_value("L2", Jf.get_L2_norm());
3320 *  
3321 * @endcode
3322 *
3323 * Stage 3. Computing @f$\vec{A}@f$.
3324 *
3325
3326 *
3327 *
3328 * @code
3329 *   std::cout << "A " << std::flush;
3330 *  
3331 *   ExactSolutions::MagneticVectorPotential A_exact;
3332 *  
3333 *   SolverA::Solver A(Settings::fe_degree,
3334 *   Settings::mapping_degree,
3335 *   T.get_tria(),
3336 *   T.get_dof_handler(),
3337 *   T.get_solution(),
3338 *   "A_p" + std::to_string(Settings::fe_degree) + "_r" +
3339 *   std::to_string(r),
3340 *   &A_exact);
3341 *  
3342 *   A.run();
3343 *  
3344 *   table_A.add_value("ndofs", A.get_n_dofs());
3345 *   table_A.add_value("ncells", A.get_n_cells());
3346 *   table_A.add_value("L2", A.get_L2_norm());
3347 *  
3348 * @endcode
3349 *
3350 * Stage 4. Computing @f$B@f$.
3351 *
3352
3353 *
3354 *
3355 * @code
3356 *   std::cout << "B " << std::flush;
3357 *  
3358 *   ExactSolutions::MagneticField B_exact;
3359 *  
3360 *   ProjectorHcurlToL2::Solver B(Settings::fe_degree,
3361 *   Settings::mapping_degree,
3362 *   T.get_tria(),
3363 *   A.get_dof_handler(),
3364 *   A.get_solution(),
3365 *   "B_p" +
3366 *   std::to_string(Settings::fe_degree) +
3367 *   "_r" + std::to_string(r),
3368 *   &B_exact);
3369 *   B.run();
3370 *  
3371 *   table_B.add_value("ndofs", B.get_n_dofs());
3372 *   table_B.add_value("ncells", B.get_n_cells());
3373 *   table_B.add_value("L2", B.get_L2_norm());
3374 * @endcode
3375 *
3376 * End stage 4.
3377 *
3378 * @code
3379 *   }
3380 *  
3381 *   table_T.save("table_T_p" + std::to_string(Settings::fe_degree + 1));
3382 *   table_Jf.save("table_Jf_p" + std::to_string(Settings::fe_degree));
3383 *   table_A.save("table_A_p" + std::to_string(Settings::fe_degree));
3384 *   table_B.save("table_B_p" + std::to_string(Settings::fe_degree));
3385 *   std::cout << std::endl;
3386 *   }
3387 *   };
3388 *  
3389 *   int main(int argc, char **argv)
3390 *   {
3391 *   try
3392 *   {
3393 * @endcode
3394 *
3395 * gmsh has global state, so set that up in the normal way:
3396 *
3397 * @code
3398 *   InitFinalize init_finalize(argc, argv, InitializeLibrary::GMSH);
3399 *   ParameterHandler parameters;
3400 *  
3401 *   MagneticProblem problem;
3402 *   problem.run();
3403 *   }
3404 *   catch (std::exception &exc)
3405 *   {
3406 *   std::cerr << std::endl
3407 *   << std::endl
3408 *   << "----------------------------------------------------"
3409 *   << std::endl;
3410 *   std::cerr << "Exception on processing: " << std::endl
3411 *   << exc.what() << std::endl
3412 *   << "Aborting!" << std::endl
3413 *   << "----------------------------------------------------"
3414 *   << std::endl;
3415 *  
3416 *   return 1;
3417 *   }
3418 *   catch (...)
3419 *   {
3420 *   std::cerr << std::endl
3421 *   << std::endl
3422 *   << "----------------------------------------------------"
3423 *   << std::endl;
3424 *   std::cerr << "Unknown exception!" << std::endl
3425 *   << "Aborting!" << std::endl
3426 *   << "----------------------------------------------------"
3427 *   << std::endl;
3428 *   return 1;
3429 *   }
3430 *  
3431 *   return 0;
3432 *   }
3433 * @endcode
3434<a name="step_98-Results"></a><h1>Results</h1>
3435
3436
3437The program generates the following output in the command line interface by
3438default.
3439
3440@code
3441Solving for (p' = 1; p = 0): T Jf A B T Jf A B T Jf A B T Jf A B
3442@endcode
3443
3444The program assumes the finite elements of the lowermost degree, i.e., @f$p' = 1@f$
3445for the FE_Q finite elements and @f$p=0@f$ for other finite elements. To change the
3446degree of the finite elements, say @f$p' = 3@f$ and @f$p = 2@f$, one needs to change
3447the setting `Settings::fe_degree = 2` and rebuild the program. The degree of the
3448FE_Q finite elements will be computed automatically as @f$p'= p + 1@f$.
3449
3450The program dumps a number of files in the current directory. In the default
3451configuration these files are:
3452- `.vtu` files. They contain the computed vector fields. Recall that the spherical
3453 manifold and transfinite interpolation manifold are attached to many cell faces.
3454 Consequently, these cell faces are curved. Furthermore, the shape functions are
3455 mapped from the reference cell to the real mesh cells by the second-order mapping
3456 to accommodate the cells with curved faces. For these reasons, one needs to use a
3457 visualization software that can deal with curved faces and the higher-order mapping.
3458 A fresh version of ParaView is recommended. Visit did not have this feature
3459 at the time this tutorial was written (in early 2026). The
3460 <a href="https://github.com/dealii/dealii/wiki/Notes-on-visualizing-high-order-output"> Notes on visualizing high order output</a>
3461 provide more information on this topic.
3462- `.tex` files. These files contain the convergence tables.
3463
3464The following provides examples of the convergence tables simulated with the
3465default settings for three different degrees of the finite elements,
3466@f$p = 0, 1, 2@f$ (recall that @f$p' = p + 1@f$).
3467
3468<table>
3469<caption>Convergence table @f$T@f$.</caption>
3470 <tr>
3471 <th>p'</th>
3472 <th>r</th>
3473 <th>cells</th>
3474 <th>dofs</th>
3475 <th>@f$\|e\|_{L^2}@f$</th>
3476 <th>@f$\alpha_{L^2}@f$</th>
3477 </tr>
3478 <tr>
3479 <td>1</td>
3480 <td>1</td>
3481 <td>144</td>
3482 <td>153</td>
3483 <td>7.47e-04</td>
3484 <td>-</td>
3485 </tr>
3486 <tr>
3487 <td>1</td>
3488 <td>2</td>
3489 <td>576</td>
3490 <td>593</td>
3491 <td>1.86e-04</td>
3492 <td>2.01</td>
3493 </tr>
3494 <tr>
3495 <td>1</td>
3496 <td>3</td>
3497 <td>2304</td>
3498 <td>2337</td>
3499 <td>4.64e-05</td>
3500 <td>2.00</td>
3501 </tr>
3502 <tr>
3503 <td>1</td>
3504 <td>4</td>
3505 <td>9216</td>
3506 <td>9281</td>
3507 <td>1.16e-05</td>
3508 <td>2.00</td>
3509 </tr>
3510 <tr>
3511 <td>2</td>
3512 <td>1</td>
3513 <td>144</td>
3514 <td>593</td>
3515 <td>1.38e-05</td>
3516 <td>-</td>
3517 </tr>
3518 <tr>
3519 <td>2</td>
3520 <td>2</td>
3521 <td>576</td>
3522 <td>2337</td>
3523 <td>8.72e-07</td>
3524 <td>3.98</td>
3525 </tr>
3526 <tr>
3527 <td>2</td>
3528 <td>3</td>
3529 <td>2304</td>
3530 <td>9281</td>
3531 <td>5.48e-08</td>
3532 <td>3.99</td>
3533 </tr>
3534 <tr>
3535 <td>2</td>
3536 <td>4</td>
3537 <td>9216</td>
3538 <td>36993</td>
3539 <td>3.45e-09</td>
3540 <td>3.99</td>
3541 </tr>
3542 <tr>
3543 <td>3</td>
3544 <td>1</td>
3545 <td>144</td>
3546 <td>1321</td>
3547 <td>1.38e-05</td>
3548 <td>-</td>
3549 </tr>
3550 <tr>
3551 <td>3</td>
3552 <td>2</td>
3553 <td>576</td>
3554 <td>5233</td>
3555 <td>8.72e-07</td>
3556 <td>3.98</td>
3557 </tr>
3558 <tr>
3559 <td>3</td>
3560 <td>3</td>
3561 <td>2304</td>
3562 <td>20833</td>
3563 <td>5.48e-08</td>
3564 <td>3.99</td>
3565 </tr>
3566 <tr>
3567 <td>3</td>
3568 <td>4</td>
3569 <td>9216</td>
3570 <td>83137</td>
3571 <td>3.51e-09</td>
3572 <td>3.97</td>
3573 </tr>
3574</table>
3575<br>
3576<table>
3577<caption>Convergence table @f$\vec{J}_f@f$.</caption>
3578 <tr>
3579 <th>p</th>
3580 <th>r</th>
3581 <th>cells</th>
3582 <th>dofs</th>
3583 <th>@f$\|e\|_{L^2}@f$</th>
3584 <th>@f$\alpha_{L^2}@f$</th>
3585 </tr>
3586 <tr>
3587 <td>0</td>
3588 <td>1</td>
3589 <td>144</td>
3590 <td>296</td>
3591 <td>2.50e-02</td>
3592 <td>-</td>
3593 </tr>
3594 <tr>
3595 <td>0</td>
3596 <td>2</td>
3597 <td>576</td>
3598 <td>1168</td>
3599 <td>1.25e-02</td>
3600 <td>1.00</td>
3601 </tr>
3602 <tr>
3603 <td>0</td>
3604 <td>3</td>
3605 <td>2304</td>
3606 <td>4640</td>
3607 <td>6.27e-03</td>
3608 <td>1.00</td>
3609 </tr>
3610 <tr>
3611 <td>0</td>
3612 <td>4</td>
3613 <td>9216</td>
3614 <td>18496</td>
3615 <td>3.13e-03</td>
3616 <td>1.00</td>
3617 </tr>
3618 <tr>
3619 <td>1</td>
3620 <td>1</td>
3621 <td>144</td>
3622 <td>1168</td>
3623 <td>3.04e-04</td>
3624 <td>-</td>
3625 </tr>
3626 <tr>
3627 <td>1</td>
3628 <td>2</td>
3629 <td>576</td>
3630 <td>4640</td>
3631 <td>3.80e-05</td>
3632 <td>3.00</td>
3633 </tr>
3634 <tr>
3635 <td>1</td>
3636 <td>3</td>
3637 <td>2304</td>
3638 <td>18496</td>
3639 <td>4.74e-06</td>
3640 <td>3.00</td>
3641 </tr>
3642 <tr>
3643 <td>1</td>
3644 <td>4</td>
3645 <td>9216</td>
3646 <td>73856</td>
3647 <td>5.91e-07</td>
3648 <td>3.00</td>
3649 </tr>
3650 <tr>
3651 <td>2</td>
3652 <td>1</td>
3653 <td>144</td>
3654 <td>2616</td>
3655 <td>3.04e-04</td>
3656 <td>-</td>
3657 </tr>
3658 <tr>
3659 <td>2</td>
3660 <td>2</td>
3661 <td>576</td>
3662 <td>10416</td>
3663 <td>3.79e-05</td>
3664 <td>3.00</td>
3665 </tr>
3666 <tr>
3667 <td>2</td>
3668 <td>3</td>
3669 <td>2304</td>
3670 <td>41568</td>
3671 <td>4.74e-06</td>
3672 <td>3.00</td>
3673 </tr>
3674 <tr>
3675 <td>2</td>
3676 <td>4</td>
3677 <td>9216</td>
3678 <td>166080</td>
3679 <td>5.91e-07</td>
3680 <td>3.00</td>
3681 </tr>
3682</table>
3683<br>
3684<table>
3685<caption> Convergence table @f$B@f$.</caption>
3686 <tr>
3687 <th>p</th>
3688 <th>r</th>
3689 <th>cells</th>
3690 <th>dofs</th>
3691 <th>@f$\|e\|_{L^2}@f$</th>
3692 <th>@f$\alpha_{L^2}@f$</th>
3693 </tr>
3694 <tr>
3695 <td>0</td>
3696 <td>1</td>
3697 <td>144</td>
3698 <td>144</td>
3699 <td>2.01e-08</td>
3700 <td>-</td>
3701 </tr>
3702 <tr>
3703 <td>0</td>
3704 <td>2</td>
3705 <td>576</td>
3706 <td>576</td>
3707 <td>9.72e-09</td>
3708 <td>1.05</td>
3709 </tr>
3710 <tr>
3711 <td>0</td>
3712 <td>3</td>
3713 <td>2304</td>
3714 <td>2304</td>
3715 <td>4.81e-09</td>
3716 <td>1.02</td>
3717 </tr>
3718 <tr>
3719 <td>0</td>
3720 <td>4</td>
3721 <td>9216</td>
3722 <td>9216</td>
3723 <td>2.40e-09</td>
3724 <td>1.00</td>
3725 </tr>
3726 <tr>
3727 <td>1</td>
3728 <td>1</td>
3729 <td>144</td>
3730 <td>576</td>
3731 <td>4.15e-10</td>
3732 <td>-</td>
3733 </tr>
3734 <tr>
3735 <td>1</td>
3736 <td>2</td>
3737 <td>576</td>
3738 <td>2304</td>
3739 <td>1.02e-10</td>
3740 <td>2.02</td>
3741 </tr>
3742 <tr>
3743 <td>1</td>
3744 <td>3</td>
3745 <td>2304</td>
3746 <td>9216</td>
3747 <td>2.54e-11</td>
3748 <td>2.01</td>
3749 </tr>
3750 <tr>
3751 <td>1</td>
3752 <td>4</td>
3753 <td>9216</td>
3754 <td>36864</td>
3755 <td>6.36e-12</td>
3756 <td>2.00</td>
3757 </tr>
3758 <tr>
3759 <td>2</td>
3760 <td>1</td>
3761 <td>144</td>
3762 <td>1296</td>
3763 <td>2.32e-11</td>
3764 <td>-</td>
3765 </tr>
3766 <tr>
3767 <td>2</td>
3768 <td>2</td>
3769 <td>576</td>
3770 <td>5184</td>
3771 <td>1.46e-12</td>
3772 <td>3.99</td>
3773 </tr>
3774 <tr>
3775 <td>2</td>
3776 <td>3</td>
3777 <td>2304</td>
3778 <td>20736</td>
3779 <td>9.15e-14</td>
3780 <td>3.99</td>
3781 </tr>
3782 <tr>
3783 <td>2</td>
3784 <td>4</td>
3785 <td>9216</td>
3786 <td>82944</td>
3787 <td>5.94e-15</td>
3788 <td>3.95</td>
3789 </tr>
3790</table>
3791
3792The following notations were used in the headers of the tables:
3793
3794- p', p - the degree of the finite elements.
3795
3796- r - the mesh refinement parameter, i.e., the number of global mesh
3797 refinements.
3798
3799- cells - the total number of active cells.
3800
3801- dofs - the number of degrees of freedom.
3802
3803-@f$\|e\|_{L^2}@f$ - the @f$L^2@f$ error norm.
3804
3805-@f$\alpha_{L^2}@f$ - the order of convergence of the @f$L^2@f$ error norm.
3806
3807Let us contemplate these convergence tables for a brief moment. The first table
3808illustrates convergence of the numerically computed current vector potential,
3809@f$T@f$. In this particular case we can expect the order of the convergence rate of
3810@f$\alpha_{L^2} \le p' + 1@f$ (See also <a href="https://www.math.colostate.edu/~bangerth/videos.676.3.95.html">video lecture 3.95</a>.) If the finite
3811elements of the lowermost degree are used, @f$p'=1@f$, the order of
3812convergence rate is at the upper boundary of the expected values,
3813@f$\alpha_{L^2} \approx 2.0@f$. If the finite elements of the second degree are
3814used, @f$p'=2@f$, the order of the convergence rate is higher than expected,
3815@f$\alpha_{L^2} \approx 4.0@f$. This means that the @f$L^2@f$ error norm converges to
3816zero at a rate higher than theoretically possible. Most likely this is due to
3817the fact that the current vector potential has a particularly simple form,
3818[Equation 3](@ref Step98_Equation_3_T). It is either constant or changes as
3819the second-order monomial,
3820\f[
3821T \sim r^2.
3822\f]
3823The second order polynomial approximates this behavior exactly. That is to
3824say, in this particular case we have a lucky situation in which the shape
3825functions can approximate the numerical solution exactly within each mesh
3826cell. This, most likely, explains the extra rapid convergence of the @f$L^2@f$
3827norm. Note also that the error norms, @f$\|e\|_{L^2}@f$, at @f$p'=2@f$ and @f$p'=3@f$
3828are the same for the same values of the mesh refinement parameter, @f$r@f$. This
3829is, most likely, due to the fact that the second-order shape functions,
3830@f$p'=2@f$, approximate the solution exactly within each mesh cell and the
3831third-order monomials of the shape function at @f$p'=3@f$ have absolutely
3832nothing to contribute to the quality of approximation.
3833
3834The second table illustrates convergence of the numerically computed
3835free-current density, @f$\vec{J}_f@f$. The free-current density has been
3836computed as a derivative of current vector potential,
3837\f[
3838\vec{J}_f = \vec{\nabla}\overset{V} {\times} T.
3839\f]
3840The derivative reduces the order of the convergence rate by one. Most
3841likely in this particular case the error made in computing @f$\vec{J}_f@f$
3842is defined by the error made in computing @f$T@f$. For this reason, the order
3843of convergence in the second table equals the order of convergence in
3844the first table minus one.
3845
3846Due to the implicit gauge we cannot observe the convergence of the magnetic
3847vector potential, @f$\vec{A}@f$. Instead, we can observe the convergence on the
3848magnetic field, @f$B@f$, given in the third table. It follows the same pattern:
3849The rate of convergence at the lower degrees of the finite elements is at
3850the best expected value, @f$\alpha_{L^2} = p + 1@f$; the rate of convergence at
3851the higher degrees of the finite elements is better than expected. The extra
3852rapid convergence at the higher degrees of the finite elements is explained
3853by the relatively simple form of the field being approximated.
3854
3855The figures below illustrate the current vector potential, @f$T@f$, the free-current
3856density, @f$\vec{J}_f@f$, and magnetic field, @f$B@f$, computed with the following
3857settings: @f$p = 2@f$ and @f$r = 4@f$. Visual inspection of the magnetic vector
3858potential, @f$\vec{A}@f$, is not very informative as its conservative portion is
3859unknown.
3860
3861@htmlonly
3862<p align="center">
3863 <img src="https://dealii.org/images/steps/developer/step-98-result-T.svg"
3864 alt="The result - current vector potential" height="531">
3865</p>
3866@endhtmlonly
3867
3868@htmlonly
3869<p align="center">
3870 <img src="https://dealii.org/images/steps/developer/step-98-result-Jf.svg"
3871 alt="The result - free current density" height="531">
3872</p>
3873@endhtmlonly
3874
3875@htmlonly
3876<p align="center">
3877 <img src="https://dealii.org/images/steps/developer/step-98-result-B.svg"
3878 alt="The result - magnetic field" height="531">
3879</p>
3880@endhtmlonly
3881
3882The images above suggest that the computed fields do not exhibit any irregular
3883behavior (the first image in the table below illustrates how irregular
3884behavior can look like). The fields on these images closely resemble the
3885corresponding closed-form analytical expressions given in the introduction.
3886From the first glans the convergence tables above may appear somewhat strange
3887due to extra rapid convergence at the higher degrees of the finite elements.
3888One, however, can argue that the extra rapid convergence can be explained by
3889the simple polynomial form of the fields being approximated. One thing is
3890certain - the convergence rates presented in these tables are at the best
3891theoretically expected values or better.
3892
3893@anchor Step98_PossibilitiesForExtensions
3894<a name="step_98-Possibilitiesforextensions"></a><h3>Possibilities for extensions</h3>
3895
3896Let us consider the two-dimensional curl-curl partial differential equation
3897again,
3898\f{equation}
3899\vec{\nabla}\overset{V}{\times}\bigg(\dfrac{1}{\mu} \vec{\nabla}\overset{S}{\times}\vec{A}\bigg)
3900+ \eta^2 \vec{A} = \vec{\nabla}\overset{V}{\times} T.
3901\f}
3902We can compute the divergence of this expression and rearrange the terms as the
3903following:
3904\f{equation}
3905\vec{\nabla}\cdot \vec{A} = \frac{1}{\eta^2} \vec{\nabla}\cdot
3906\bigg[\vec{\nabla}\overset{V}{\times} T -
3907\vec{\nabla}\overset{V}{\times}\bigg(\dfrac{1}{\mu}
3908\vec{\nabla}\overset{S}{\times}\vec{A}\bigg)\bigg].
3909\f}
3910The divergence of the vector curl equals zero, see the introduction. Therefore,
3911the right-hand side of the last equation equals zero,
3912\f{equation}
3913\vec{\nabla}\cdot \vec{A} = 0.
3914\f}
3915That is to say, the @f$\eta^2@f$ gauging term can be considered to be the Coulomb
3916gauge at least in theory. In practice the situation is a bit more complicated.
3917What the @f$\eta^2@f$ gauging term does depends on the value of @f$\eta^2@f$. The table
3918below attempts to express this very point.
3919@htmlonly
3920<p align="center">
3921 <img src="https://dealii.org/images/steps/developer/step-98-eta.svg"
3922 alt="The result - eta-squared table" height="1062">
3923</p>
3924@endhtmlonly
3925
3926This table presents four simulations for four different values of @f$\eta^2@f$. In
3927all four simulations the degree of the finite elements and the mesh refinement
3928parameter were @f$p=2@f$ and @f$r=1@f$, respectively. The setting
3929`Settings::project_exact_solution` was set to `true`.
3930
3931At the setting @f$\eta^2=\dfrac{10^{-10}}{\mu_0}@f$ the computed magnetic vector
3932potential (the orange curve on the @f$|\vec{A}(x,0)|@f$ plot) looks exactly the same
3933as the projected exact solution gauged by the Coulomb gauge (the blue curve). At
3934this value of @f$\eta^2@f$ the term @f$\eta^2\vec{A}@f$ acts like the Coulomb gauge.
3935At the default setting, @f$\eta^2=0@f$, the solution, @f$\vec{A}@f$, is contaminated by
3936an unknown conservative vector field. The computed magnetic field, @f$B@f$, at this
3937setting looks exactly as the corresponding exact solution because the
3938conservative portion of @f$\vec{A}@f$ is filtered out by the process of computing
3939the magnetic field, @f$B = \vec{\nabla}\overset{S}{\times}\vec{A}@f$. At the setting
3940@f$\eta^2=\dfrac{10^{-6}}{\mu_0}@f$ the solenoidal part of the computed magnetic
3941vector potential deviates from the exact solution. We can deduce this from the
3942fact that the computed magnetic
3943field deviates from the exact expression for the magnetic field on the @f$B(x,0)@f$
3944plot. The error in the solenoidal part of the computed @f$\vec{A}@f$ is due to
3945the fact that introduction of the gauging term, @f$\eta^2\vec{A}@f$, modifies the
3946initial
3947curl-curl equation. So, strictly speaking, we are solving a different partial
3948differential equation. If @f$\eta^2@f$ is small, the difference between the initial
3949curl-curl equation and the curl-curl equation modified by adding the gauging
3950term is negligible. Consequently, the error in the solenoidal part of the
3951computed @f$\vec{A}@f$ is negligible as well. Evidently, in this particular case
3952"small" means @f$\eta^2 \ll \dfrac{10^{-6}}{\mu_0}@f$.
3953
3954Normally, we are interested in measurable fields such as magnetic field and
3955consider the magnetic vector potential as a useful tool for computing measurable
3956fields. We can tolerate the presence of an unknown conservative component in the
3957magnetic vector potential, i.e., the situation illustrated by the first two rows
3958in the table above. In such disposition we need to keep @f$\eta^2@f$ as small as
3959possible, i.e., as far away as possible from the situation shown in last row of
3960the table. In this tutorial program we set @f$\eta^2@f$ to zero and increase it just
3961a bit in if the conjugate gradient algorithm cannot converge.
3962
3963Suppose for a moment that we would like to have the solution to the curl-curl
3964equation in terms of a purely solenoidal magnetic vector potential, @f$\vec{A}@f$,
3965that is, a solution gauged by the Coulomb gauge. To get such a solution we need
3966to tweak the @f$\eta^2@f$ parameter. By contemplating
3967the table above one can hypothesize that there exists an optimal value of the
3968gauging parameter, @f$\eta^2_\text{opt}@f$, at which the @f$L^2@f$ error norm computed for
3969@f$\vec{A}@f$ is minimal. The optimal value should be somewhere in between
3970@f$\eta^2=\dfrac{10^{-12}}{\mu_0}@f$ and @f$\eta^2=\dfrac{10^{-6}}{\mu_0}@f$, based on the
3971experiments above. Try to
3972verify this hypothesis by finding the exact value of @f$\eta^2_\text{opt}@f$.
3973
3974In this instance we have a close-form analytical expression of the exact
3975solution, i.e., the expression of @f$\vec{A}@f$ given in the introduction. In a
3976real-life simulation there is no expression of the exact solution. Try to
3977think of a method of blind (meaning without the exact solution) estimation of
3978@f$\eta^2_\text{opt}@f$. Try to implement and test your ideas.
3979
3980Adding the gauging term, @f$\eta^2\vec{A}@f$, to the curl-curl equation converts
3981a positive semidefinite system matrix into a positive definite matrix. Strictly
3982speaking, adding the gauging term modifies the initial curl-curl equation. For
3983this reason, it is important to keep @f$\eta^2@f$ small so the solution is not
3984afflicted by the error induced by adding the gauging term. The definition of
3985"small" here is a bit fuzzy. In absence of the exact solution setting @f$\eta^2@f$
3986at the acceptable level of the error in the solenoidal component of @f$\vec{A}@f$
3987is difficult. It is better to discard the gauging term and use a more
3988sophisticated linear solver. The hypre AMS @cite hypre1998b can solve the
3989systems of linear equations yielded by the curl-curl equation without the
3990@f$\eta^2@f$ gauging term, (@f$\beta=0@f$ in @cite hypre1998b). Try to implement the
3991hypre AMS.
3992 *
3993 *
3994<a name="step_98-PlainProg"></a>
3995<h1> The plain program</h1>
3996@include "step-98.cc"
3997*/
*  iterator end()
*  *  for(const auto &cell :triangulation.active_cell_iterators())
*  *  int main(int argc, char **argv)
*  *  iterator begin()
*  x_component_mask set(0, true)
*  *  *  struct InterferenceTaperTransform *  
*  *  iterator()=default
ConvergenceTable()=default
void evaluate_convergence_rates(const std::string &data_column_key, const std::string &reference_column_key, const RateMode rate_mode, const unsigned int dim=2)
void add_data_vector(const VectorType &data, const std::vector< std::string > &names, const DataVectorType type=type_automatic, const std::vector< DataComponentInterpretation::DataComponentInterpretation > &data_component_interpretation={})
Definition fe_q.h:552
virtual void vector_value_list(const std::vector< Point< dim > > &points, std::vector< Vector< RangeNumberType > > &values) const
void attach_triangulation(Triangulation< dim, spacedim > &tria)
Definition grid_in.cc:155
static void set_thread_limit(const unsigned int max_threads=numbers::invalid_unsigned_int)
Definition point.h:111
void initialize(const MatrixType &A, const AdditionalData &parameters=AdditionalData())
void write_tex(std::ostream &file, const bool with_header=true) const
void set_column_order(const std::vector< std::string > &new_order)
void set_tex_caption(const std::string &key, const std::string &tex_caption)
void set_scientific(const std::string &key, const bool scientific)
void set_precision(const std::string &key, const unsigned int precision)
void initialize(const Triangulation< dim, spacedim > &triangulation)
Point< 2 > second
Definition grid_out.cc:4640
Point< 2 > first
Definition grid_out.cc:4639
unsigned int level
Definition grid_out.cc:4642
#define Assert(cond, exc)
typename ActiveSelector::active_cell_iterator active_cell_iterator
void loop(IteratorType begin, std_cxx20::type_identity_t< IteratorType > end, DOFINFO &dinfo, INFOBOX &info, const std::function< void(std_cxx20::type_identity_t< DOFINFO > &, typename INFOBOX::CellInfo &)> &cell_worker, const std::function< void(std_cxx20::type_identity_t< DOFINFO > &, typename INFOBOX::CellInfo &)> &boundary_worker, const std::function< void(std_cxx20::type_identity_t< DOFINFO > &, std_cxx20::type_identity_t< DOFINFO > &, typename INFOBOX::CellInfo &, typename INFOBOX::CellInfo &)> &face_worker, AssemblerType &assembler, const LoopControl &lctrl=LoopControl())
Definition loop.h:562
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)
UpdateFlags
@ update_values
Shape function values.
@ update_JxW_values
Transformed quadrature weights.
@ update_gradients
Shape function gradients.
@ update_quadrature_points
Transformed quadrature points.
@ update_default
No update.
std::vector< index_type > data
Definition mpi.cc:734
std::size_t size
Definition mpi.cc:733
std::vector< value_type > split(const typename ::Triangulation< dim, spacedim >::cell_iterator &parent, const value_type parent_value)
void approximate(const SynchronousIterators< std::tuple< typename DoFHandler< dim, spacedim >::active_cell_iterator, Vector< float >::iterator > > &cell, const Mapping< dim, spacedim > &mapping, const DoFHandler< dim, spacedim > &dof_handler, const InputVector &solution, const unsigned int component)
@ matrix
Contents is actually a matrix.
constexpr char L
constexpr char T
constexpr char A
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
void L2(Vector< number > &result, const FEValuesBase< dim > &fe, const std::vector< double > &input, const double factor=1.)
Definition l2.h:157
Point< spacedim > point(const gp_Pnt &p, const double tolerance=1e-10)
Definition utilities.cc:210
SymmetricTensor< 2, dim, Number > e(const Tensor< 2, dim, Number > &F)
SymmetricTensor< 2, dim, Number > b(const Tensor< 2, dim, Number > &F)
*  *  *  ScaleZFunction< dim, Number, components >::ScaleZFunction *  component(component)
*  *  if(update_pressure &update_flags) *  compute_pressure(constitutive_request
*  *  *  *  *  *  TimeRateUpdateFlags TimeRateRequest< ValueType, dim, Number >  get_update_flags() const
*  *  *  *  std::vector< Number > ThermoPlasticMaterial< dim, ViscoplasticYieldLaw, Number >::get_state_parameters   const
void apply(const Kokkos::TeamPolicy< MemorySpace::Default::kokkos_space::execution_space >::member_type &team_member, const Kokkos::View< Number *, ShapeDataMemorySpace > shape_data, const ViewTypeIn in, ViewTypeOut out)
double compute_global_error(const Triangulation< dim, spacedim > &tria, const InVector &cellwise_error, const NormType &norm, const double exponent=2.)
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 integrate_difference(const Mapping< dim, spacedim > &mapping, const DoFHandler< dim, spacedim > &dof, const ReadVector< Number > &fe_function, const Function< spacedim, Number > &exact_solution, OutVector &difference, const Quadrature< dim > &q, const NormType &norm, const Function< spacedim, double > *weight=nullptr, const double exponent=2.)
void project(const Mapping< dim, spacedim > &mapping, const DoFHandler< dim, spacedim > &dof, const AffineConstraints< typename VectorType::value_type > &constraints, const Quadrature< dim > &quadrature, const Function< spacedim, typename VectorType::value_type > &function, VectorType &vec, const bool enforce_zero_boundary=false, const Quadrature< dim - 1 > &q_boundary=(dim > 1 ? QGauss< dim - 1 >(2) :Quadrature< dim - 1 >()), const bool project_to_boundary_first=false)
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)
void run(const std::vector< std::vector< Iterator > > &colored_iterators, Worker worker, Copier copier, const ScratchData &sample_scratch_data, const CopyData &sample_copy_data, const unsigned int queue_length=2 *MultithreadInfo::n_threads(), const unsigned int chunk_size=8)
void load(Archive &ar, ::std_cxx26::inplace_vector< T, N > &vec, const unsigned int)
void save(Archive &ar, const ::std_cxx26::inplace_vector< T, N > &vec, const unsigned int)
void copy(const T *begin, const T *end, U *dest)
int(&) functions(const void *v1, const void *v2)
void assemble(const MeshWorker::DoFInfoBox< dim, DOFINFO > &dinfo, A *assembler)
Definition loop.h:68
unsigned int get_degree(const std::vector< typename BarycentricPolynomials< dim >::PolyType > &polys)
constexpr types::manifold_id flat_manifold_id
Definition types.h:332
STL namespace.
::VectorizedArray< Number, width > pow(const ::VectorizedArray< Number, width > &, const Number p)
Definition types.h:30
constexpr Number determinant(const SymmetricTensor< 2, dim, Number > &)