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