deal.II version GIT relicensing-6809-ge913b9bb34 2026-09-25 17:20:01+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
step-97.h
Go to the documentation of this file.
1
1555 *  
1556 * @endcode
1557 *
1558 * The following three get-functions are used to channel the mesh and the
1559 * solution to the next solver.
1560 *
1561 * @code
1562 *   const Triangulation<3> &get_tria() const
1563 *   {
1564 *   return triangulation;
1565 *   }
1566 *   const DoFHandler<3> &get_dof_handler() const
1567 *   {
1568 *   return dof_handler;
1569 *   }
1570 *   const Vector<double> &get_solution() const
1571 *   {
1572 *   return solution;
1573 *   }
1574 *  
1575 *   private:
1576 * @endcode
1577 *
1578 * The following data members are typical for all deal.II simulations:
1579 * triangulation, finite elements, dof handlers, etc. The constraints
1580 * are used to enforce the Dirichlet boundary conditions. The names of the
1581 * data members are self-explanatory.
1582 *
1583
1584 *
1585 *
1586 * @code
1587 *   Triangulation<3> triangulation;
1588 *  
1589 *   const FE_Nedelec<3> fe;
1590 *   DoFHandler<3> dof_handler;
1591 *   Vector<double> solution;
1592 *  
1593 *   SparseMatrix<double> system_matrix;
1594 *   Vector<double> system_rhs;
1595 *  
1596 *   AffineConstraints<double> constraints;
1597 *   SparsityPattern sparsity_pattern;
1598 *  
1599 *   SphericalManifold<3> sphere;
1600 *  
1601 *   const unsigned int refinement_parameter;
1602 *   const unsigned int mapping_degree;
1603 *   const double eta_squared;
1604 *   const std::string fname;
1605 *  
1606 * @endcode
1607 *
1608 * The program utilizes the WorkStream technology. The @ref step_9 "step-9" tutorial
1609 * does a much better job of explaining the workings of WorkStream.
1610 * Reading the @ref workstream_paper "WorkStream paper" is recommended.
1611 * The following structures and functions are related to WorkStream.
1612 *
1613 * @code
1614 *   struct AssemblyScratchData
1615 *   {
1616 *   AssemblyScratchData(const FiniteElement<3> &fe,
1617 *   const double eta_squared,
1618 *   const unsigned int mapping_degree);
1619 *  
1620 *   AssemblyScratchData(const AssemblyScratchData &scratch_data);
1621 *  
1622 *   const FreeCurrentDensity Jf;
1623 *  
1624 *   MappingQ<3> mapping;
1625 *   FEValues<3> fe_values;
1626 *  
1627 *   const unsigned int dofs_per_cell;
1628 *   const unsigned int n_q_points;
1629 *  
1630 *   std::vector<Tensor<1, 3>> Jf_list;
1631 *  
1632 *   const double eta_squared;
1633 *   };
1634 *  
1635 *   struct AssemblyCopyData
1636 *   {
1638 *   Vector<double> cell_rhs;
1639 *   std::vector<types::global_dof_index> local_dof_indices;
1640 *   };
1641 *  
1642 *   void system_matrix_local(
1643 *   const typename DoFHandler<3>::active_cell_iterator &cell,
1644 *   AssemblyScratchData &scratch_data,
1645 *   AssemblyCopyData &copy_data);
1646 *  
1647 *   void copy_local_to_global(const AssemblyCopyData &copy_data);
1648 *   };
1649 *  
1650 *   Solver::Solver(const unsigned int p,
1651 *   const unsigned int r,
1652 *   const unsigned int mapping_degree,
1653 *   const double eta_squared,
1654 *   const std::string &fname)
1655 *   : fe(p)
1656 *   , refinement_parameter(r)
1657 *   , mapping_degree(mapping_degree)
1658 *   , eta_squared(eta_squared)
1659 *   , fname(fname)
1660 *   {}
1661 *  
1662 * @endcode
1663 *
1664 * The following function loads the mesh, assigns material IDs to all cells,
1665 * and attaches the spherical manifold to the mesh. The material IDs are
1666 * assigned on the basis of the distance from the center of a cell to the
1667 * origin. The spherical manifold is attached to a face if all vertices of
1668 * the face are at the same distance from the origin provided the cell is
1669 * outside the cube in the center of the mesh, see mesh description in the
1670 * introduction.
1671 *
1672 * @code
1673 *   void Solver::make_mesh()
1674 *   {
1675 *   GridIn<3> gridin;
1676 *  
1677 *   gridin.attach_triangulation(triangulation);
1678 *   std::ifstream ifs("sphere_r" + std::to_string(refinement_parameter) +
1679 *   ".msh");
1680 *   gridin.read_msh(ifs);
1681 *  
1682 *   triangulation.reset_all_manifolds();
1683 *  
1684 *   for (auto cell : triangulation.active_cell_iterators())
1685 *   {
1686 *   cell->set_material_id(
1687 *   Settings::material_id_free_space); // The cell is in free space.
1688 *  
1689 *   if ((cell->center().norm() > Settings::a1) &&
1690 *   (cell->center().norm() < Settings::b1))
1691 *   cell->set_material_id(
1692 *   Settings::material_id_core); // The cell is inside the core.
1693 *  
1694 *   if ((cell->center().norm() > Settings::a2) &&
1695 *   (cell->center().norm() < Settings::b2))
1696 *   cell->set_material_id(
1697 *   Settings::material_id_free_current); /* The cell is inside the Jf
1698 *   region. */
1699 *  
1700 *   for (unsigned int f = 0; f < cell->n_faces(); f++)
1701 *   {
1702 *   double dif_norm = 0.0;
1703 *   for (unsigned int v = 1; v < cell->face(f)->n_vertices(); v++)
1704 *   dif_norm += std::abs(cell->face(f)->vertex(0).norm() -
1705 *   cell->face(f)->vertex(v).norm());
1706 *  
1707 *   if ((dif_norm < Settings::eps) &&
1708 *   (cell->center().norm() > Settings::d1))
1709 *   cell->face(f)->set_all_manifold_ids(1);
1710 *   }
1711 *   }
1712 *  
1713 *   triangulation.set_manifold(1, sphere);
1714 *   }
1715 *  
1716 * @endcode
1717 *
1718 * The following function initializes the dofs, applies the Dirichlet
1719 * boundary condition, and initializes the vectors and matrices. The first
1720 * two lines of the code initialize the dof handler and distribute the dofs.
1721 * The segment of the code in between `constraints.clear()` and
1722 * `constraints.close()` applies the homogeneous Dirichlet boundary condition.
1723 * As discussed in the introduction, the Dirichlet boundary condition is an
1724 * essential condition and must be enforced by constraining the system matrix.
1725 * This segment of the code does the constraining. The rest of the function
1726 * arranges the dofs in a sparsity pattern and initializes the system matrices
1727 * and vectors.
1728 *
1729 * @code
1730 *   void Solver::setup()
1731 *   {
1732 *   dof_handler.reinit(triangulation);
1733 *   dof_handler.distribute_dofs(fe);
1734 *  
1735 *   constraints.clear();
1736 *  
1737 *   DoFTools::make_hanging_node_constraints(dof_handler, constraints);
1738 *  
1740 *   dof_handler,
1741 *   0, // The first vector component.
1743 *   Settings::boundary_id_infinity,
1744 *   constraints,
1745 *   MappingQ<3>(mapping_degree));
1746 *  
1747 *   constraints.close();
1748 *  
1749 *   DynamicSparsityPattern dsp(dof_handler.n_dofs(), dof_handler.n_dofs());
1750 *   DoFTools::make_sparsity_pattern(dof_handler, dsp, constraints, false);
1751 *   sparsity_pattern.copy_from(dsp);
1752 *  
1753 *   system_matrix.reinit(sparsity_pattern);
1754 *   solution.reinit(dof_handler.n_dofs());
1755 *   system_rhs.reinit(dof_handler.n_dofs());
1756 *   }
1757 *  
1758 * @endcode
1759 *
1760 * Formally, the following function assembles the system of linear equations.
1761 * In reality, however, it just spells all the magic words to get the
1762 * WorkStream going. The interesting part, i.e., the actual assembling of the
1763 * system matrix and the right-hand side, happens below in the function
1764 * `Solver::system_matrix_local()`.
1765 *
1766 * @code
1767 *   void Solver::assemble()
1768 *   {
1769 *   WorkStream::run(dof_handler.begin_active(),
1770 *   dof_handler.end(),
1771 *   *this,
1772 *   &Solver::system_matrix_local,
1773 *   &Solver::copy_local_to_global,
1774 *   AssemblyScratchData(fe, eta_squared, mapping_degree),
1775 *   AssemblyCopyData());
1776 *   }
1777 *  
1778 * @endcode
1779 *
1780 * The following two constructors initialize scratch data from the input
1781 * parameters and from another object of the same type, i.e., a copy
1782 * constructor.
1783 *
1784 * @code
1785 *   Solver::AssemblyScratchData::AssemblyScratchData(
1786 *   const FiniteElement<3> &fe,
1787 *   const double eta_squared,
1788 *   const unsigned int mapping_degree)
1789 *   : Jf()
1790 *   , mapping(mapping_degree)
1791 *   , fe_values(mapping,
1792 *   fe,
1793 *   QGauss<3>(fe.degree + 2),
1796 *   , dofs_per_cell(fe_values.dofs_per_cell)
1797 *   , n_q_points(fe_values.get_quadrature().size())
1798 *   , Jf_list(n_q_points, Tensor<1, 3>())
1799 *   , eta_squared(eta_squared)
1800 *   {}
1801 *  
1802 *   Solver::AssemblyScratchData::AssemblyScratchData(
1803 *   const AssemblyScratchData &scratch_data)
1804 *   : Jf()
1805 *   , mapping(scratch_data.mapping.get_degree())
1806 *   , fe_values(mapping,
1807 *   scratch_data.fe_values.get_fe(),
1808 *   scratch_data.fe_values.get_quadrature(),
1811 *   , dofs_per_cell(fe_values.dofs_per_cell)
1812 *   , n_q_points(fe_values.get_quadrature().size())
1813 *   , Jf_list(n_q_points, Tensor<1, 3>())
1814 *   , eta_squared(scratch_data.eta_squared)
1815 *   {}
1816 *  
1817 * @endcode
1818 *
1819 * The following function assembles a fraction of the system matrix and the
1820 * system right-hand side related to a single cell. These fractions are
1821 * `copy_data.cell_matrix` and `copy_data.cell_rhs`. They are copied into
1822 * the system matrix, @f$A_{ij}@f$, and the right-hand side, @f$b_i@f$, by the
1823 * function `Solver::copy_local_to_global()`.
1824 *
1825
1826 *
1827 * In the first four statements of the function we reinitialize the matrices
1828 * and vectors related to the current cell and compute the finite element
1829 * values. Next, we compute the free-current density, @f$\vec{J}_f@f$, at the
1830 * quadrature points by calling `scratch_data.Jf.value_list`. After that we
1831 * declare a vector values extractor, `ve`, and compute the
1832 * [components](@ref Step97_Numerical_Recipe_T) of the cell matrix and cell
1833 * right-hand side in the nested `for` loops. The labels of the integrals are
1834 * the same as in the introduction to this tutorial. In the last line of the
1835 * function we query the dof indices on the current cell and store them in the
1836 * copy data structure, so we know to which locations of the system matrix and
1837 * right-hand side the components of the cell matrix and cell right-hand side
1838 * must be copied.
1839 *
1840 * @code
1841 *   void Solver::system_matrix_local(
1842 *   const typename DoFHandler<3>::active_cell_iterator &cell,
1843 *   AssemblyScratchData &scratch_data,
1844 *   AssemblyCopyData &copy_data)
1845 *   {
1846 *   copy_data.cell_matrix.reinit(scratch_data.dofs_per_cell,
1847 *   scratch_data.dofs_per_cell);
1848 *  
1849 *   copy_data.cell_rhs.reinit(scratch_data.dofs_per_cell);
1850 *  
1851 *   copy_data.local_dof_indices.resize(scratch_data.dofs_per_cell);
1852 *  
1853 *   scratch_data.fe_values.reinit(cell);
1854 *  
1855 *   scratch_data.Jf.value_list(scratch_data.fe_values.get_quadrature_points(),
1856 *   cell->material_id(),
1857 *   scratch_data.Jf_list);
1858 *  
1859 *   const FEValuesExtractors::Vector ve(0);
1860 *  
1861 *   for (unsigned int q_index = 0; q_index < scratch_data.n_q_points; ++q_index)
1862 *   {
1863 *   for (unsigned int i = 0; i < scratch_data.dofs_per_cell; ++i)
1864 *   {
1865 *   for (unsigned int j = 0; j < scratch_data.dofs_per_cell; ++j)
1866 *   {
1867 *   copy_data.cell_matrix(i, j) += // Integral I_a1+I_a3.
1868 *   (scratch_data.fe_values[ve].curl(i,
1869 *   q_index) * // curl phi_i(x_q)
1870 *   scratch_data.fe_values[ve].curl(j,
1871 *   q_index) // curl phi_j(x_q)
1872 *   +
1873 *   scratch_data.eta_squared * // eta^2
1874 *   scratch_data.fe_values[ve].value(i,
1875 *   q_index) * // phi_i(x_q)
1876 *   scratch_data.fe_values[ve].value(j, q_index) // phi_j(x_q)
1877 *   ) *
1878 *   scratch_data.fe_values.JxW(q_index); // dx
1879 *   }
1880 *   copy_data.cell_rhs(i) += // Integral I_b3-1.
1881 *   (scratch_data.Jf_list[q_index] *
1882 *   scratch_data.fe_values[ve].curl(i, q_index)) *
1883 *   scratch_data.fe_values.JxW(
1884 *   q_index); // J_f(x_q).(curl phi_i(x_q))dx.
1885 *   }
1886 *   }
1887 *  
1888 *   cell->get_dof_indices(copy_data.local_dof_indices);
1889 *   }
1890 *  
1891 * @endcode
1892 *
1893 * The following function copies the components of a cell matrix and a cell
1894 * right-hand side into the system matrix, @f$A_{ij}@f$, and the system right-hand
1895 * side, @f$b_i@f$.
1896 *
1897 * @code
1898 *   void Solver::copy_local_to_global(const AssemblyCopyData &copy_data)
1899 *   {
1900 *   constraints.distribute_local_to_global(copy_data.cell_matrix,
1901 *   copy_data.cell_rhs,
1902 *   copy_data.local_dof_indices,
1903 *   system_matrix,
1904 *   system_rhs);
1905 *   }
1906 *  
1907 * @endcode
1908 *
1909 * The following function solves the system of linear equations. In theory,
1910 * the CG solver can solve an @f$m \times m@f$ system of linear equations in at
1911 * most @f$m@f$ steps. Accordingly, we set the maximum number of iteration steps
1912 * to `system_rhs.size()`. The stopping condition is
1913 * \f[\|\boldsymbol{b} - \boldsymbol{A}\boldsymbol{c}\| < 10^{-6}
1914 * \|\boldsymbol{b}\|.\f] As soon as we use constraints, we must not forget to
1915 * distribute them.
1916 *
1917 * @code
1918 *   void Solver::solve()
1919 *   {
1920 *   SolverControl control(system_rhs.size(), 1.0e-6 * system_rhs.l2_norm());
1921 *  
1923 *   SolverCG<Vector<double>> cg(control, memory);
1924 *  
1925 *   PreconditionSSOR<SparseMatrix<double>> preconditioner;
1926 *   preconditioner.initialize(system_matrix, 1.2);
1927 *  
1928 *   cg.solve(system_matrix, solution, system_rhs, preconditioner);
1929 *  
1930 *   constraints.distribute(solution);
1931 *   }
1932 *  
1933 * @endcode
1934 *
1935 * The following function saves the computed current vector potential into a
1936 * `.vtu` file.
1937 *
1938 * @code
1939 *   void Solver::output_results() const
1940 *   {
1941 *   const std::vector<std::string> solution_names(3, "VectorField");
1942 *   const std::vector<DataComponentInterpretation::DataComponentInterpretation>
1943 *   interpretation(3,
1945 *  
1946 *   DataOut<3> data_out;
1947 *  
1948 *   data_out.add_data_vector(dof_handler,
1949 *   solution,
1950 *   solution_names,
1951 *   interpretation);
1952 *  
1953 *   DataOutBase::VtkFlags flags;
1954 *   flags.write_higher_order_cells = true;
1955 *   data_out.set_flags(flags);
1956 *  
1957 *   const MappingQ<3> mapping(mapping_degree);
1958 *  
1959 *   data_out.build_patches(mapping,
1960 *   fe.degree + 2,
1962 *  
1963 *   std::ofstream ofs(fname + ".vtu");
1964 *   data_out.write_vtu(ofs);
1965 *   }
1966 *  
1967 * @endcode
1968 *
1969 * The `run` function below aggregates all the computation steps in the
1970 * correct order.
1971 *
1972 * @code
1973 *   void Solver::run()
1974 *   {
1975 *   make_mesh();
1976 *   setup();
1977 *   assemble();
1978 *   solve();
1979 *   output_results();
1980 *   clear();
1981 *   }
1982 *   } // namespace SolverT
1983 *  
1984 * @endcode
1985 *
1986 *
1987 * <a name="step_97-SolverA"></a>
1988 * <h3>Solver - A</h3>
1989 *
1990
1991 *
1992 * The following namespace contains all the code related to the computation of
1993 * the magnetic vector potential, @f$\vec{A}@f$. The main difference between this
1994 * solver and the solver for the current vector potential, @f$\vec{T}@f$, is in how
1995 * the information on the source is fed to respective solvers. The solver for
1996 * @f$\vec{T}@f$ is fed data sampled from the analytical closed-form expression for
1997 * @f$\vec{J}_f@f$. The solver for @f$\vec{A}@f$ is fed a field function, i.e., a
1998 * numerically computed current vector potential, @f$\vec{T}@f$.
1999 *
2000 * @code
2001 *   namespace SolverA
2002 *   {
2003 * @endcode
2004 *
2005 * The following class describes the permeability in the entire problem
2006 * domain. The permeability is [given](@ref Step97_Equation_MU) by the
2007 * definition of the problem, see the introduction.
2008 *
2009 * @code
2010 *   class Permeability
2011 *   {
2012 *   public:
2013 *   void value_list(const types::material_id mid,
2014 *   std::vector<double> &values) const
2015 *   {
2016 *   if ((mid == Settings::material_id_free_space) ||
2017 *   (mid == Settings::material_id_free_current))
2018 *   std::fill(values.begin(), values.end(), Settings::mu_0);
2019 *  
2020 *   if (mid == Settings::material_id_core)
2021 *   std::fill(values.begin(), values.end(), Settings::mu_1);
2022 *   }
2023 *   };
2024 *  
2025 * @endcode
2026 *
2027 * The following class describes the parameter @f$\gamma@f$ in the Robin boundary
2028 * condition. As soon as it is evaluated on the boundary, the permeability
2029 * equals to that of free space. Therefore, we evaluate the parameter gamma as
2030 * \f[
2031 * \gamma = \dfrac{1}{\mu_0 r}.
2032 * \f]
2033 *
2034 * @code
2035 *   class Gamma
2036 *   {
2037 *   public:
2038 *   void value_list(const std::vector<Point<3>> &r,
2039 *   std::vector<double> &values) const
2040 *   {
2041 *   Assert(r.size() == values.size(),
2042 *   ExcDimensionMismatch(r.size(), values.size()));
2043 *  
2044 *   for (unsigned int i = 0; i < values.size(); i++)
2045 *   values[i] = 1.0 / (Settings::mu_0 * r[i].norm());
2046 *   }
2047 *   };
2048 *  
2049 * @endcode
2050 *
2051 * The following class implements the solver that minimizes the
2052 * [functional](@ref Step97_Functional_A) @f$F(\vec{A})@f$. The numerically
2053 * computed current vector potential, @f$\vec{T}@f$, is fed to this solver by
2054 * means of the input parameters `dof_handler_T` and `solution_T`. Moreover,
2055 * this solver reuses the mesh on which @f$\vec{T}@f$ has been computed. The
2056 * reference to the mesh is passed via the input parameter `triangulation_T`.
2057 *
2058 * @code
2059 *   class Solver
2060 *   {
2061 *   public:
2062 *   Solver() = delete;
2063 *   Solver(const unsigned int p, // Degree of the Nedelec finite elements.
2064 *   const unsigned int mapping_degree,
2065 *   const Triangulation<3> &triangulation_T,
2066 *   const DoFHandler<3> &dof_handler_T,
2067 *   const Vector<double> &solution_T,
2068 *   const double eta_squared = 0.0,
2069 *   const std::string &fname = "data");
2070 *  
2071 *   void setup(); // Initializes dofs, vectors, matrices.
2072 *   void assemble(); // Assembles the system of linear equations.
2073 *   void solve(); // Solves the system of linear equations.
2074 *   void output_results() const; // Saves computed A into a vtu file.
2075 *   void clear() // Clears the memory for the next solver.
2076 *   {
2077 *   system_matrix.clear();
2078 *   system_rhs.reinit(0);
2079 *   }
2080 *   void run(); /* Executes the last five functions in the proper order
2081 *   and measures the execution time for each function. */
2082 *  
2083 *   const DoFHandler<3> &get_dof_handler() const
2084 *   {
2085 *   return dof_handler;
2086 *   }
2087 *   const Vector<double> &get_solution() const
2088 *   {
2089 *   return solution;
2090 *   }
2091 *  
2092 *   private:
2093 *   const Triangulation<3> &triangulation_T;
2094 *   const DoFHandler<3> &dof_handler_T;
2095 *   const Vector<double> &solution_T;
2096 *  
2097 * @endcode
2098 *
2099 * The following data members are typical for all deal.II simulations:
2100 * triangulation, finite elements, dof handlers, etc. The constraints
2101 * are used to enforce the Dirichlet boundary conditions. The names of the
2102 * data members are self-explanatory.
2103 *
2104 * @code
2105 *   const FE_Nedelec<3> fe;
2106 *   DoFHandler<3> dof_handler;
2107 *   Vector<double> solution;
2108 *  
2109 *   SparseMatrix<double> system_matrix;
2110 *   Vector<double> system_rhs;
2111 *  
2112 *   AffineConstraints<double> constraints;
2113 *   SparsityPattern sparsity_pattern;
2114 *  
2115 *   const unsigned int mapping_degree;
2116 *   const double eta_squared;
2117 *   const std::string fname;
2118 *  
2119 * @endcode
2120 *
2121 * This time we have two dof handlers, `dof_handler_T` for @f$\vec{T}@f$ and
2122 * `dof_handler` for @f$\vec{A}@f$. The WorkStream needs to walk through
2123 * the two dof handlers synchronously. For this purpose we will pair two
2124 * active cell iterators (one from `dof_handler_T`, another from
2125 * `dof_handler`). For that we need the `IteratorPair` type.
2126 *
2127 * @code
2128 *   using IteratorTuple =
2129 *   std::tuple<typename DoFHandler<3>::active_cell_iterator,
2131 *  
2132 *   using IteratorPair = SynchronousIterators<IteratorTuple>;
2133 *  
2134 * @endcode
2135 *
2136 * The program utilizes the WorkStream technology. The @ref step_9 "step-9" tutorial
2137 * does a much better job of explaining the workings of WorkStream.
2138 * Reading the @ref workstream_paper "WorkStream paper" is recommended.
2139 * The following structures and functions are related to WorkStream.
2140 *
2141 * @code
2142 *   struct AssemblyScratchData
2143 *   {
2144 *   AssemblyScratchData(const FiniteElement<3> &fe,
2145 *   const DoFHandler<3> &dof_hand_T,
2146 *   const Vector<double> &dofs_T,
2147 *   const unsigned int mapping_degree,
2148 *   const double eta_squared,
2149 *   const BoundaryConditionType boundary_condition_type);
2150 *  
2151 *   AssemblyScratchData(const AssemblyScratchData &scratch_data);
2152 *  
2153 *   const Permeability permeability;
2154 *   const Gamma gamma;
2155 *  
2156 *   MappingQ<3> mapping;
2157 *   FEValues<3> fe_values;
2158 *   FEFaceValues<3> fe_face_values;
2159 *  
2160 *   FEValues<3> fe_values_T;
2161 *  
2162 *   const unsigned int dofs_per_cell;
2163 *   const unsigned int n_q_points;
2164 *   const unsigned int n_q_points_face;
2165 *  
2166 *   std::vector<double> permeability_list;
2167 *   std::vector<double> gamma_list;
2168 *   std::vector<Tensor<1, 3>> T_values;
2169 *  
2170 *   const DoFHandler<3> &dof_hand_T;
2171 *   const Vector<double> &dofs_T;
2172 *  
2173 *   const double eta_squared;
2174 *   const BoundaryConditionType boundary_condition_type;
2175 *   };
2176 *  
2177 *   struct AssemblyCopyData
2178 *   {
2180 *   Vector<double> cell_rhs;
2181 *   std::vector<types::global_dof_index> local_dof_indices;
2182 *   };
2183 *  
2184 *   void system_matrix_local(const IteratorPair &IP,
2185 *   AssemblyScratchData &scratch_data,
2186 *   AssemblyCopyData &copy_data);
2187 *  
2188 *   void copy_local_to_global(const AssemblyCopyData &copy_data);
2189 *   };
2190 *  
2191 *   Solver::Solver(const unsigned int p,
2192 *   const unsigned int mapping_degree,
2193 *   const Triangulation<3> &triangulation_T,
2194 *   const DoFHandler<3> &dof_handler_T,
2195 *   const Vector<double> &solution_T,
2196 *   const double eta_squared,
2197 *   const std::string &fname)
2198 *   : triangulation_T(triangulation_T)
2199 *   , dof_handler_T(dof_handler_T)
2200 *   , solution_T(solution_T)
2201 *   , fe(p)
2202 *   , mapping_degree(mapping_degree)
2203 *   , eta_squared(eta_squared)
2204 *   , fname(fname)
2205 *   {}
2206 *  
2207 * @endcode
2208 *
2209 * The following function initializes the dofs, applies the Dirichlet
2210 * boundary condition, and initializes the vectors and matrices. The first
2211 * two lines of the code initialize the dof handler and distribute the dofs.
2212 * The segment of the code in between `constraints.clear()` and
2213 * `constraints.close()` applies the homogeneous Dirichlet boundary condition.
2214 * As discussed in the introduction, the Dirichlet boundary condition is an
2215 * essential condition and must be enforced by constraining the system matrix.
2216 * This segment of the code does the constraining. The program can be
2217 * [switched](@ref Step97_TXT_BCSwitch) between the Dirichlet, Neumann, and
2218 * Robin boundary conditions. For this reason, we use the `if` statement to
2219 * make sure that the system matrix is constrained only if the Dirichlet
2220 * boundary condition is chosen by the user. The rest of the function arranges
2221 * the dofs in a sparsity pattern and initializes the system matrices and
2222 * vectors.
2223 *
2224 * @code
2225 *   void Solver::setup()
2226 *   {
2227 *   dof_handler.reinit(triangulation_T);
2228 *   dof_handler.distribute_dofs(fe);
2229 *  
2230 *   constraints.clear();
2231 *  
2232 *   DoFTools::make_hanging_node_constraints(dof_handler, constraints);
2233 *  
2234 *   if (Settings::boundary_condition_type_A == Dirichlet)
2236 *   dof_handler,
2237 *   0, // The first vector component.
2239 *   Settings::boundary_id_infinity,
2240 *   constraints,
2241 *   MappingQ<3>(mapping_degree));
2242 *  
2243 *   constraints.close();
2244 *  
2245 *   DynamicSparsityPattern dsp(dof_handler.n_dofs(), dof_handler.n_dofs());
2246 *   DoFTools::make_sparsity_pattern(dof_handler, dsp, constraints, false);
2247 *  
2248 *   sparsity_pattern.copy_from(dsp);
2249 *   system_matrix.reinit(sparsity_pattern);
2250 *   solution.reinit(dof_handler.n_dofs());
2251 *   system_rhs.reinit(dof_handler.n_dofs());
2252 *   }
2253 *  
2254 * @endcode
2255 *
2256 * Formally, the following function assembles the system of linear equations.
2257 * In reality, however, it just spells all the magic words to get the
2258 * WorkStream going. The interesting part, i.e., the actual assembling of the
2259 * system matrix and the right-hand side happens below in the
2260 * `Solver::system_matrix_local` function. Note that this time the first two
2261 * input parameters to `WorkStream::run` are pairs of iterators, not
2262 * iterators themselves as per usual. Note also the order in which we package
2263 * the iterators: first the iterator of `dof_handler`, then the iterator of
2264 * the `dof_handler_T`. We will extract them in the same order.
2265 *
2266 * @code
2267 *   void Solver::assemble()
2268 *   {
2269 *   WorkStream::run(IteratorPair({dof_handler.begin_active(),
2270 *   dof_handler_T.begin_active()}),
2271 *   IteratorPair({dof_handler.end(), dof_handler_T.end()}),
2272 *   *this,
2273 *   &Solver::system_matrix_local,
2274 *   &Solver::copy_local_to_global,
2275 *   AssemblyScratchData(fe,
2276 *   dof_handler_T,
2277 *   solution_T,
2278 *   mapping_degree,
2279 *   eta_squared,
2280 *   Settings::boundary_condition_type_A),
2281 *   AssemblyCopyData());
2282 *   }
2283 *  
2284 * @endcode
2285 *
2286 * The following two constructors initialize scratch data from the input
2287 * parameters and from another object of the same type, i.e., a copy
2288 * constructor.
2289 *
2290 * @code
2291 *   Solver::AssemblyScratchData::AssemblyScratchData(
2292 *   const FiniteElement<3> &fe,
2293 *   const DoFHandler<3> &dof_hand_T,
2294 *   const Vector<double> &dofs_T,
2295 *   const unsigned int mapping_degree,
2296 *   const double eta_squared,
2297 *   const BoundaryConditionType boundary_condition_type)
2298 *   : permeability()
2299 *   , gamma()
2300 *   , mapping(mapping_degree)
2301 *   , fe_values(mapping,
2302 *   fe,
2303 *   QGauss<3>(fe.degree + 2),
2305 *   , fe_face_values(mapping,
2306 *   fe,
2307 *   QGauss<2>(fe.degree + 2),
2310 *   , fe_values_T(mapping,
2311 *   dof_hand_T.get_fe(),
2312 *   QGauss<3>(fe.degree + 2),
2314 *   , dofs_per_cell(fe_values.dofs_per_cell)
2315 *   , n_q_points(fe_values.get_quadrature().size())
2316 *   , n_q_points_face(fe_face_values.get_quadrature().size())
2317 *   , permeability_list(n_q_points)
2318 *   , gamma_list(n_q_points_face)
2319 *   , T_values(n_q_points)
2320 *   , dof_hand_T(dof_hand_T)
2321 *   , dofs_T(dofs_T)
2322 *   , eta_squared(eta_squared)
2323 *   , boundary_condition_type(boundary_condition_type)
2324 *   {}
2325 *  
2326 *   Solver::AssemblyScratchData::AssemblyScratchData(
2327 *   const AssemblyScratchData &scratch_data)
2328 *   : permeability()
2329 *   , gamma()
2330 *   , mapping(scratch_data.mapping.get_degree())
2331 *   , fe_values(mapping,
2332 *   scratch_data.fe_values.get_fe(),
2333 *   scratch_data.fe_values.get_quadrature(),
2335 *   , fe_face_values(mapping,
2336 *   scratch_data.fe_face_values.get_fe(),
2337 *   scratch_data.fe_face_values.get_quadrature(),
2340 *   , fe_values_T(mapping,
2341 *   scratch_data.fe_values_T.get_fe(),
2342 *   scratch_data.fe_values_T.get_quadrature(),
2344 *   , dofs_per_cell(fe_values.dofs_per_cell)
2345 *   , n_q_points(fe_values.get_quadrature().size())
2346 *   , n_q_points_face(fe_face_values.get_quadrature().size())
2347 *   , permeability_list(n_q_points)
2348 *   , gamma_list(n_q_points_face)
2349 *   , T_values(n_q_points)
2350 *   , dof_hand_T(scratch_data.dof_hand_T)
2351 *   , dofs_T(scratch_data.dofs_T)
2352 *   , eta_squared(scratch_data.eta_squared)
2353 *   , boundary_condition_type(scratch_data.boundary_condition_type)
2354 *   {}
2355 *  
2356 * @endcode
2357 *
2358 * The following function assembles a fraction of the system matrix and the
2359 * system right-hand side related to a single cell. These fractions are
2360 * `copy_data.cell_matrix` and `copy_data.cell_rhs`. They are copied into
2361 * to the system matrix, @f$A_{ij}@f$, and the right-hand side, @f$b_i@f$, by the
2362 * function `Solver::copy_local_to_global()`.
2363 *
2364
2365 *
2366 * In the first three statements of the function we reinitialize the matrices
2367 * and vectors related to the current cell. Next, we extract the cells from
2368 * the cell pair. We extract them in the correct order, see above. After that
2369 * we compute the finite element values for both types of the finite elements,
2370 * compute the permeability, declare the vector values extractor, `ve`, and
2371 * compute current vector potential, @f$\vec{T}@f$, at quadrature points. Next,
2372 * we compute the three [volume integrals](@ref Step97_Numerical_Recipe_A),
2373 * @f$I_{a1}@f$, @f$I_{a3}@f$, and @f$I_{b3-1}@f$ in the three nested `for` loops. The
2374 * labels of the integrals are the same as in the introduction to this
2375 * tutorial. The program can be [switched](@ref Step97_TXT_BCSwitch) between
2376 * the Dirichlet, Neumann, and Robin boundary conditions. For this reason, we
2377 * compute the surface integral @f$I_{a2}@f$ only if the Robin boundary condition
2378 * is chosen by the user. For this reason, we use the `if` statement to make
2379 * sure that the surface integral @f$I_{a2}@f$ is added to the components of the
2380 * system matrix only if the Robin boundary condition is chosen by the user.
2381 * In all other cases the integral @f$I_{a2}@f$ is omitted. Omitting the @f$I_{a2}@f$
2382 * integral is as good as setting @f$\gamma = 0@f$ in the
2383 * [boundary value problem](@ref Step97_BVP_A). In the last line of the
2384 * function we query the dof indices on the current cell and store them in
2385 * the copy data structure, so we know to which locations of the system
2386 * matrix and right-hand side the components of the cell matrix and cell
2387 * right-hand side must be copied.
2388 *
2389 * @code
2390 *   void Solver::system_matrix_local(const IteratorPair &IP,
2391 *   AssemblyScratchData &scratch_data,
2392 *   AssemblyCopyData &copy_data)
2393 *   {
2394 *   copy_data.cell_matrix.reinit(scratch_data.dofs_per_cell,
2395 *   scratch_data.dofs_per_cell);
2396 *  
2397 *   copy_data.cell_rhs.reinit(scratch_data.dofs_per_cell);
2398 *  
2399 *   copy_data.local_dof_indices.resize(scratch_data.dofs_per_cell);
2400 *  
2401 *   const typename DoFHandler<3>::active_cell_iterator cell = std::get<0>(*IP);
2402 *   const typename DoFHandler<3>::active_cell_iterator cell_T =
2403 *   std::get<1>(*IP);
2404 *  
2405 *   scratch_data.fe_values.reinit(cell);
2406 *   scratch_data.fe_values_T.reinit(cell_T);
2407 *  
2408 *   scratch_data.permeability.value_list(cell->material_id(),
2409 *   scratch_data.permeability_list);
2410 *  
2411 *   const FEValuesExtractors::Vector ve(0);
2412 *  
2413 *   scratch_data.fe_values_T[ve].get_function_values(scratch_data.dofs_T,
2414 *   scratch_data.T_values);
2415 *  
2416 *   for (unsigned int q_index = 0; q_index < scratch_data.n_q_points; ++q_index)
2417 *   {
2418 *   for (unsigned int i = 0; i < scratch_data.dofs_per_cell; ++i)
2419 *   {
2420 *   for (unsigned int j = 0; j < scratch_data.dofs_per_cell; ++j)
2421 *   {
2422 *   copy_data.cell_matrix(i, j) += // Integral I_a1+I_a3.
2423 *   (1.0 / scratch_data.permeability_list[q_index]) * // 1 / mu
2424 *   (scratch_data.fe_values[ve].curl(i,
2425 *   q_index) * // curl phi_i(x_q)
2426 *   scratch_data.fe_values[ve].curl(j,
2427 *   q_index) // curl phi_j(x_q)
2428 *   +
2429 *   scratch_data.eta_squared * // eta^2
2430 *   scratch_data.fe_values[ve].value(i,
2431 *   q_index) * // phi_i(x_q)
2432 *   scratch_data.fe_values[ve].value(j, q_index) // phi_j(x_q)
2433 *   ) *
2434 *   scratch_data.fe_values.JxW(q_index); // dx
2435 *   }
2436 *   copy_data.cell_rhs(i) += // Integral I_b3-1.
2437 *   (scratch_data.T_values[q_index] *
2438 *   scratch_data.fe_values[ve].curl(i, q_index)) *
2439 *   scratch_data.fe_values.JxW(q_index); // T(x_q).(curl phi_i(x_q))dx
2440 *   }
2441 *   }
2442 *  
2443 *   if (scratch_data.boundary_condition_type == BoundaryConditionType::Robin)
2444 *   {
2445 *   for (unsigned int f = 0; f < cell->n_faces(); ++f)
2446 *   {
2447 *   if (cell->face(f)->at_boundary())
2448 *   {
2449 *   scratch_data.fe_face_values.reinit(cell, f);
2450 *  
2451 *   for (unsigned int q_index_face = 0;
2452 *   q_index_face < scratch_data.n_q_points_face;
2453 *   ++q_index_face)
2454 *   {
2455 *   for (unsigned int i = 0; i < scratch_data.dofs_per_cell;
2456 *   ++i)
2457 *   {
2458 *   scratch_data.gamma.value_list(
2459 *   scratch_data.fe_face_values.get_quadrature_points(),
2460 *   scratch_data.gamma_list);
2461 *  
2462 *   for (unsigned int j = 0; j < scratch_data.dofs_per_cell;
2463 *   ++j)
2464 *   {
2465 *   copy_data.cell_matrix(i, j) += // Integral I_a2.
2466 *   scratch_data.gamma_list[q_index_face] * // gamma
2467 *   (cross_product_3d(scratch_data.fe_face_values
2468 *   .normal_vector(q_index_face),
2469 *   scratch_data.fe_face_values[ve]
2470 *   .value(i, q_index_face)) *
2471 *   cross_product_3d(scratch_data.fe_face_values
2472 *   .normal_vector(q_index_face),
2473 *   scratch_data.fe_face_values[ve]
2474 *   .value(j, q_index_face)))
2475 *   /* (n x phi_i(x_q)).(n x phi_j(x_q)) */
2476 *   * scratch_data.fe_face_values.JxW(
2477 *   q_index_face); // dx
2478 *   } // for j
2479 *   } // for i
2480 *   } // for q_index_face
2481 *   } // if (cell->face(f)->at_boundary())
2482 *   } // for f
2483 *   } /* if (scratch_data.boundary_condition_type ==
2484 *   BoundaryConditionType::Robin) */
2485 *  
2486 *   cell->get_dof_indices(copy_data.local_dof_indices);
2487 *   }
2488 *  
2489 * @endcode
2490 *
2491 * This function copies the components of a cell matrix and a cell right-hand
2492 * side into the system matrix, @f$A_{ij}@f$, and the system right-hand side,
2493 * @f$b_i@f$.
2494 *
2495 * @code
2496 *   void Solver::copy_local_to_global(const AssemblyCopyData &copy_data)
2497 *   {
2498 *   constraints.distribute_local_to_global(copy_data.cell_matrix,
2499 *   copy_data.cell_rhs,
2500 *   copy_data.local_dof_indices,
2501 *   system_matrix,
2502 *   system_rhs);
2503 *   }
2504 *  
2505 * @endcode
2506 *
2507 * The following function solves the system of linear equations. In theory,
2508 * the CG solver can solve an @f$m \times m@f$ system of linear equations in at
2509 * most @f$m@f$ steps. Accordingly, we set the maximum number of iteration steps
2510 * to `system_rhs.size()`. The stopping condition is
2511 * \f[\|\boldsymbol{b} - \boldsymbol{A}\boldsymbol{c}\| < 10^{-6}
2512 * \|\boldsymbol{b}\|.\f] As soon as we use constraints, we must not forget to
2513 * distribute them.
2514 *
2515 * @code
2516 *   void Solver::solve()
2517 *   {
2518 *   SolverControl control(system_rhs.size(), 1.0e-6 * system_rhs.l2_norm());
2519 *  
2521 *   SolverCG<Vector<double>> cg(control, memory);
2522 *  
2523 *   PreconditionSSOR<SparseMatrix<double>> preconditioner;
2524 *   preconditioner.initialize(system_matrix, 1.2);
2525 *  
2526 *   cg.solve(system_matrix, solution, system_rhs, preconditioner);
2527 *  
2528 *   constraints.distribute(solution);
2529 *   }
2530 *  
2531 * @endcode
2532 *
2533 * The following function saves the computed magnetic vector potential into a
2534 * `.vtu` file.
2535 *
2536 * @code
2537 *   void Solver::output_results() const
2538 *   {
2539 *   std::vector<std::string> solution_names(3, "VectorField");
2540 *   std::vector<DataComponentInterpretation::DataComponentInterpretation>
2541 *   interpretation(3,
2543 *  
2544 *   DataOut<3> data_out;
2545 *  
2546 *   data_out.add_data_vector(dof_handler,
2547 *   solution,
2548 *   solution_names,
2549 *   interpretation);
2550 *  
2551 *   DataOutBase::VtkFlags flags;
2552 *   flags.write_higher_order_cells = true;
2553 *   data_out.set_flags(flags);
2554 *  
2555 *   const MappingQ<3> mapping(mapping_degree);
2556 *  
2557 *   data_out.build_patches(mapping,
2558 *   fe.degree + 2,
2560 *  
2561 *   std::ofstream ofs(fname + ".vtu");
2562 *   data_out.write_vtu(ofs);
2563 *   }
2564 *  
2565 * @endcode
2566 *
2567 * The `run` function below aggregates all the computation steps in the
2568 * correct order.
2569 *
2570 * @code
2571 *   void Solver::run()
2572 *   {
2573 *   setup();
2574 *   assemble();
2575 *   solve();
2576 *   output_results();
2577 *   clear();
2578 *   }
2579 *   } // namespace SolverA
2580 *  
2581 *  
2582 * @endcode
2583 *
2584 *
2585 * <a name="step_97-ProjectorfromHcurltoHdiv"></a>
2586 * <h3>Projector from H(curl) to H(div)</h3>
2587 * The following namespace contains all the code related to the conversion of
2588 * the magnetic vector potential, @f$\vec{A}@f$, into magnetic field, @f$\vec{B}@f$.
2589 * The magnetic vector potential is modeled by the FE_Nedelec finite elements,
2590 * while the magnetic field is modeled by the FE_RaviartThomas finite elements.
2591 * This code is also used for converting the current vector potential,
2592 * @f$\vec{T}@f$, into the free-current density, @f$\vec{J}_f@f$.
2593 *
2594 * @code
2595 *   namespace ProjectorHcurlToHdiv
2596 *   {
2597 *  
2598 * @endcode
2599 *
2600 * This class implements the solver that minimizes the
2601 * [functional](@ref Step97_Functional_B) @f$F(\vec{B})@f$ or @f$F(\vec{J}_f)@f$, see
2602 * the introduction. The input vector field, @f$\vec{A}@f$ or @f$\vec{T}@f$, is fed to
2603 * the solver by means of the input parameters `dof_handler_Hcurl` and
2604 * `solution_Hcurl`. Moreover, this solver reuses the mesh on which the input
2605 * vector field has been computed. The reference to the mesh is passed via the
2606 * input parameter `triangulation_Hcurl`. There are no constraints this time
2607 * around as we are not going to apply the Dirichlet boundary condition.
2608 *
2609 * @code
2610 *   class Solver
2611 *   {
2612 *   public:
2613 *   Solver() = delete;
2614 *   Solver(const unsigned int p, // Degree of the Raviart-Thomas finite elements
2615 *   const unsigned int mapping_degree,
2616 *   const Triangulation<3> &triangulation_Hcurl,
2617 *   const DoFHandler<3> &dof_handler_Hcurl,
2618 *   const Vector<double> &solution_Hcurl,
2619 *   const std::string &fname = "data",
2620 *   const Function<3> *exact_solution = nullptr);
2621 *  
2622 *   double get_L2_norm()
2623 *   {
2624 *   return L2_norm;
2625 *   };
2626 *  
2627 *   unsigned int get_n_cells() const
2628 *   {
2629 *   return triangulation_Hcurl.n_active_cells();
2630 *   }
2631 *  
2632 *   types::global_dof_index get_n_dofs() const
2633 *   {
2634 *   return dof_handler_Hdiv.n_dofs();
2635 *   }
2636 *  
2637 *   void setup(); // Initializes dofs, vectors, matrices.
2638 *   void assemble(); // Assembles the system of linear equations.
2639 *   void solve(); // Solves the system of linear equations.
2640 *   void output_results() const; // Saves computed Jf or B into a vtu file.
2641 *   void compute_error_norms(); // Computes L^2 error norm.
2642 *   void project_exact_solution_fcn(); // Projects exact solution.
2643 *   void clear()
2644 *   {
2645 *   system_matrix.clear();
2646 *   system_rhs.reinit(0);
2647 *   }
2648 *   void run(); /* Executes the last seven functions in the proper order
2649 *   and measures the execution time for each function. */
2650 *  
2651 *   private:
2652 *   const Triangulation<3> &triangulation_Hcurl;
2653 *   const DoFHandler<3> &dof_handler_Hcurl;
2654 *   const Vector<double> &solution_Hcurl;
2655 *  
2656 * @endcode
2657 *
2658 * The following data members are typical for all deal.II simulations:
2659 * triangulation, finite elements, dof handlers, etc. The constraints
2660 * are used to enforce the Dirichlet boundary conditions. The names of the
2661 * data members are self-explanatory.
2662 *
2663 * @code
2664 *   const FE_RaviartThomas<3> fe_Hdiv;
2665 *   DoFHandler<3> dof_handler_Hdiv;
2666 *  
2667 *   SparsityPattern sparsity_pattern;
2668 *   SparseMatrix<double> system_matrix;
2669 *  
2670 *   Vector<double> solution_Hdiv;
2671 *   Vector<double> system_rhs;
2672 *  
2673 *   Vector<double> projected_exact_solution;
2674 *  
2675 *   AffineConstraints<double> constraints;
2676 *  
2677 *   const Function<3> *exact_solution;
2678 *  
2679 *   const unsigned int mapping_degree;
2680 *  
2681 *   Vector<double> L2_per_cell;
2682 *   double L2_norm;
2683 *  
2684 *   const std::string fname;
2685 *  
2686 * @endcode
2687 *
2688 * This time we have two dof handlers, `dof_handler_Hcurl` for the input
2689 * vector field and `dof_handler_Hdiv` for the output vector field. The
2690 * WorkStream needs to walk through the two dof handlers synchronously.
2691 * For this purpose we will pair two active cells iterators (one from
2692 * `dof_handler_Hcurl`, another from `dof_handler_Hdiv`) to be walked
2693 * through synchronously. For that we need the `IteratorPair` type.
2694 *
2695 * @code
2696 *   using IteratorTuple =
2697 *   std::tuple<typename DoFHandler<3>::active_cell_iterator,
2699 *  
2700 *   using IteratorPair = SynchronousIterators<IteratorTuple>;
2701 *  
2702 * @endcode
2703 *
2704 * The program utilizes the WorkStream technology. The @ref step_9 "step-9" tutorial
2705 * does a much better job of explaining the workings of WarkStream.
2706 * Reading the @ref workstream_paper "WorkStream paper" is recommended.
2707 * The following structures and functions are related to WorkStream.
2708 *
2709 * @code
2710 *   struct AssemblyScratchData
2711 *   {
2712 *   AssemblyScratchData(const FiniteElement<3> &fe,
2713 *   const DoFHandler<3> &dof_handr_Hcurl,
2714 *   const Vector<double> &dofs_Hcurl,
2715 *   const unsigned int mapping_degree);
2716 *  
2717 *   AssemblyScratchData(const AssemblyScratchData &scratch_data);
2718 *  
2719 *   MappingQ<3> mapping;
2720 *   FEValues<3> fe_values_Hdiv;
2721 *   FEValues<3> fe_values_Hcurl;
2722 *  
2723 *   const unsigned int dofs_per_cell;
2724 *   const unsigned int n_q_points;
2725 *  
2726 *   std::vector<Tensor<1, 3>> curl_vec_in_Hcurl;
2727 *  
2728 *   const DoFHandler<3> &dof_hand_Hcurl;
2729 *   const Vector<double> &dofs_Hcurl;
2730 *   };
2731 *  
2732 *   struct AssemblyCopyData
2733 *   {
2735 *   Vector<double> cell_rhs;
2736 *   std::vector<types::global_dof_index> local_dof_indices;
2737 *   };
2738 *  
2739 *   void system_matrix_local(const IteratorPair &IP,
2740 *   AssemblyScratchData &scratch_data,
2741 *   AssemblyCopyData &copy_data);
2742 *  
2743 *   void copy_local_to_global(const AssemblyCopyData &copy_data);
2744 *   };
2745 *  
2746 *   Solver::Solver(const unsigned int p,
2747 *   const unsigned int mapping_degree,
2748 *   const Triangulation<3> &triangulation_Hcurl,
2749 *   const DoFHandler<3> &dof_handler_Hcurl,
2750 *   const Vector<double> &solution_Hcurl,
2751 *   const std::string &fname,
2752 *   const Function<3> *exact_solution)
2753 *   : triangulation_Hcurl(triangulation_Hcurl)
2754 *   , dof_handler_Hcurl(dof_handler_Hcurl)
2755 *   , solution_Hcurl(solution_Hcurl)
2756 *   , fe_Hdiv(p)
2757 *   , exact_solution(exact_solution)
2758 *   , mapping_degree(mapping_degree)
2759 *   , fname(fname)
2760 *   {
2761 *   Assert(exact_solution != nullptr,
2762 *   ExcMessage("The exact solution is missing."));
2763 *   }
2764 *  
2765 * @endcode
2766 *
2767 * The following function initializes the dofs, vectors and matrices. This
2768 * time there are no constraints as we do not apply Dirichlet boundary
2769 * condition.
2770 *
2771 * @code
2772 *   void Solver::setup()
2773 *   {
2774 *   constraints.close();
2775 *  
2776 *   dof_handler_Hdiv.reinit(triangulation_Hcurl);
2777 *   dof_handler_Hdiv.distribute_dofs(fe_Hdiv);
2778 *  
2779 *   DynamicSparsityPattern dsp(dof_handler_Hdiv.n_dofs(),
2780 *   dof_handler_Hdiv.n_dofs());
2781 *   DoFTools::make_sparsity_pattern(dof_handler_Hdiv, dsp, constraints, false);
2782 *  
2783 *   sparsity_pattern.copy_from(dsp);
2784 *   system_matrix.reinit(sparsity_pattern);
2785 *   solution_Hdiv.reinit(dof_handler_Hdiv.n_dofs());
2786 *   system_rhs.reinit(dof_handler_Hdiv.n_dofs());
2787 *  
2788 *   if (Settings::project_exact_solution && exact_solution)
2789 *   projected_exact_solution.reinit(dof_handler_Hdiv.n_dofs());
2790 *  
2791 *   if (exact_solution)
2792 *   L2_per_cell.reinit(triangulation_Hcurl.n_active_cells());
2793 *   }
2794 *  
2795 * @endcode
2796 *
2797 * Formally, the following function assembles the system of linear equations.
2798 * In reality, however, it just spells all the magic words to get the
2799 * WorkStream going. The interesting part, i.e., the actual assembling of the
2800 * system matrix and the right-hand side happens below in the
2801 * `Solver::system_matrix_local` function. Note that this time the first two
2802 * input parameters to `WorkStream::run` are pairs of iterators, not
2803 * iterators themselves as per usual. Note also the order in which we package
2804 * the iterators: first the iterator of `dof_handler_Hdiv`, then the iterator
2805 * of the `dof_handler_Hcurl`. We will extract them in the same order.
2806 *
2807 * @code
2808 *   void Solver::assemble()
2809 *   {
2810 *   WorkStream::run(IteratorPair({dof_handler_Hdiv.begin_active(),
2811 *   dof_handler_Hcurl.begin_active()}),
2812 *   IteratorPair(
2813 *   {dof_handler_Hdiv.end(), dof_handler_Hcurl.end()}),
2814 *   *this,
2815 *   &Solver::system_matrix_local,
2816 *   &Solver::copy_local_to_global,
2817 *   AssemblyScratchData(fe_Hdiv,
2818 *   dof_handler_Hcurl,
2819 *   solution_Hcurl,
2820 *   mapping_degree),
2821 *   AssemblyCopyData());
2822 *   }
2823 *  
2824 * @endcode
2825 *
2826 * The following two constructors initialize scratch data from the input
2827 * parameters and from another object of the same type, i.e., a copy
2828 * constructor.
2829 *
2830 * @code
2831 *   Solver::AssemblyScratchData::AssemblyScratchData(
2832 *   const FiniteElement<3> &fe,
2833 *   const DoFHandler<3> &dof_hand_Hcurl,
2834 *   const Vector<double> &dofs_Hcurl,
2835 *   const unsigned int mapping_degree)
2836 *   : mapping(mapping_degree)
2837 *   , fe_values_Hdiv(mapping,
2838 *   fe,
2839 *   QGauss<3>(fe.degree + 2),
2841 *   , fe_values_Hcurl(mapping,
2842 *   dof_hand_Hcurl.get_fe(),
2843 *   QGauss<3>(fe.degree + 2),
2845 *   , dofs_per_cell(fe_values_Hdiv.dofs_per_cell)
2846 *   , n_q_points(fe_values_Hdiv.get_quadrature().size())
2847 *   , curl_vec_in_Hcurl(n_q_points)
2848 *   , dof_hand_Hcurl(dof_hand_Hcurl)
2849 *   , dofs_Hcurl(dofs_Hcurl)
2850 *   {}
2851 *  
2852 *   Solver::AssemblyScratchData::AssemblyScratchData(
2853 *   const AssemblyScratchData &scratch_data)
2854 *   : mapping(scratch_data.mapping.get_degree())
2855 *   , fe_values_Hdiv(mapping,
2856 *   scratch_data.fe_values_Hdiv.get_fe(),
2857 *   scratch_data.fe_values_Hdiv.get_quadrature(),
2859 *   , fe_values_Hcurl(mapping,
2860 *   scratch_data.fe_values_Hcurl.get_fe(),
2861 *   scratch_data.fe_values_Hcurl.get_quadrature(),
2863 *   , dofs_per_cell(fe_values_Hdiv.dofs_per_cell)
2864 *   , n_q_points(fe_values_Hdiv.get_quadrature().size())
2865 *   , curl_vec_in_Hcurl(scratch_data.n_q_points)
2866 *   , dof_hand_Hcurl(scratch_data.dof_hand_Hcurl)
2867 *   , dofs_Hcurl(scratch_data.dofs_Hcurl)
2868 *   {}
2869 *  
2870 * @endcode
2871 *
2872 * The following function assembles a fraction of the system matrix and the
2873 * system right-hand side related to a single cell. These fractions are
2874 * `copy_data.cell_matrix` and `copy_data.cell_rhs`. They are copied into
2875 * to the system matrix, @f$A_{ij}@f$, and the right-hand side, @f$b_i@f$, by the
2876 * function `Solver::copy_local_to_global()`.
2877 *
2878
2879 *
2880 * First, we reinitialize the matrices and vectors related to the current
2881 * cell, update the finite element values, and compute the curl of the input
2882 * vector field at quadrature points. The variable
2883 * `scratch_data.curl_vec_in_Hcurl` denotes the curl of the input vector
2884 * field, @f$\vec{\nabla} \times \vec{T}@f$ or @f$\vec{\nabla} \times \vec{A}@f$,
2885 * depending on the context. Second, we compute the
2886 * [components](@ref Step97_Numerical_Recipe_B) of the cell matrix and cell
2887 * right-hand side in the three nested `for` loops. The labels of the
2888 * integrals are the same as in the introduction to this tutorial. Third, we
2889 * query the dof indices on the current cell and store them in the copy data
2890 * structure, so we know to which locations of the system matrix and
2891 * right-hand side the components of the cell matrix and cell right-hand side
2892 * must be copied.
2893 *
2894 * @code
2895 *   void Solver::system_matrix_local(const IteratorPair &IP,
2896 *   AssemblyScratchData &scratch_data,
2897 *   AssemblyCopyData &copy_data)
2898 *   {
2899 *   copy_data.cell_matrix.reinit(scratch_data.dofs_per_cell,
2900 *   scratch_data.dofs_per_cell);
2901 *  
2902 *   copy_data.cell_rhs.reinit(scratch_data.dofs_per_cell);
2903 *  
2904 *   copy_data.local_dof_indices.resize(scratch_data.dofs_per_cell);
2905 *  
2906 *   const typename DoFHandler<3>::active_cell_iterator cell_Hdiv =
2907 *   std::get<0>(*IP);
2908 *   const typename DoFHandler<3>::active_cell_iterator cell_Hcurl =
2909 *   std::get<1>(*IP);
2910 *  
2911 *   scratch_data.fe_values_Hdiv.reinit(cell_Hdiv);
2912 *   scratch_data.fe_values_Hcurl.reinit(cell_Hcurl);
2913 *  
2914 *   const FEValuesExtractors::Vector ve(0);
2915 *  
2916 *   scratch_data.fe_values_Hcurl[ve].get_function_curls(
2917 *   scratch_data.dofs_Hcurl, scratch_data.curl_vec_in_Hcurl);
2918 *  
2919 *   for (unsigned int q_index = 0; q_index < scratch_data.n_q_points; ++q_index)
2920 *   {
2921 *   for (unsigned int i = 0; i < scratch_data.dofs_per_cell; ++i)
2922 *   {
2923 *   for (unsigned int j = 0; j < scratch_data.dofs_per_cell; ++j)
2924 *   {
2925 *   copy_data.cell_matrix(i, j) += // Integral I_a
2926 *   scratch_data.fe_values_Hdiv[ve].value(i,
2927 *   q_index) * // phi_i(x_q)
2928 *   scratch_data.fe_values_Hdiv[ve].value(j,
2929 *   q_index) * // phi_j(x_q)
2930 *   scratch_data.fe_values_Hdiv.JxW(q_index); // dx
2931 *   }
2932 *  
2933 *   copy_data.cell_rhs(i) += // Integral I_b
2934 *   scratch_data
2935 *   .curl_vec_in_Hcurl[q_index] * /* curl A(x_q) OR curl T(x_q),
2936 *   depending on the context. */
2937 *   scratch_data.fe_values_Hdiv[ve].value(i, q_index) * // phi_i(x_q)
2938 *   scratch_data.fe_values_Hdiv.JxW(q_index); // dx
2939 *   }
2940 *   }
2941 *  
2942 *   cell_Hdiv->get_dof_indices(copy_data.local_dof_indices);
2943 *   }
2944 *  
2945 * @endcode
2946 *
2947 * The following function copies the components of a cell matrix and a cell
2948 * right-hand side into the system matrix, @f$A_{ij}@f$, and the system right-hand
2949 * side, @f$b_i@f$.
2950 *
2951 * @code
2952 *   void Solver::copy_local_to_global(const AssemblyCopyData &copy_data)
2953 *   {
2954 *   constraints.distribute_local_to_global(copy_data.cell_matrix,
2955 *   copy_data.cell_rhs,
2956 *   copy_data.local_dof_indices,
2957 *   system_matrix,
2958 *   system_rhs);
2959 *   }
2960 *  
2961 * @endcode
2962 *
2963 * The following two functions compute the error norms and project the exact
2964 * solution.
2965 *
2966 * @code
2967 *   void Solver::compute_error_norms()
2968 *   {
2969 *   if (exact_solution)
2970 *   {
2971 *   const Weight weight;
2972 *   const Function<3, double> *mask = &weight;
2973 *  
2975 *   dof_handler_Hdiv,
2976 *   solution_Hdiv,
2977 *   *exact_solution,
2978 *   L2_per_cell,
2979 *   QGauss<3>(fe_Hdiv.degree + 4),
2981 *   mask);
2982 *  
2983 *   L2_norm = VectorTools::compute_global_error(triangulation_Hcurl,
2984 *   L2_per_cell,
2986 *   }
2987 *   }
2988 *  
2989 *   void Solver::project_exact_solution_fcn()
2990 *   {
2991 *   if (Settings::project_exact_solution && exact_solution)
2992 *   {
2993 *   AffineConstraints<double> constraints_empty;
2994 *  
2995 *   constraints_empty.clear();
2996 *  
2998 *   constraints_empty);
2999 *  
3000 *   constraints_empty.close();
3001 *  
3002 *   VectorTools::project(MappingQ<3>(mapping_degree),
3003 *   dof_handler_Hdiv,
3004 *   constraints_empty,
3005 *   QGauss<3>(fe_Hdiv.degree + 2),
3006 *   *exact_solution,
3007 *   projected_exact_solution);
3008 *   }
3009 *   }
3010 *  
3011 * @endcode
3012 *
3013 * The following function solves the system of linear equations. In theory,
3014 * the CG solver can solve an @f$m \times m@f$ system of linear equations in at
3015 * most @f$m@f$ steps. Accordingly, we set the maximum number of iteration steps
3016 * to `system_rhs.size()`. The stopping condition is
3017 * \f[\|\boldsymbol{b} - \boldsymbol{A}\boldsymbol{c}\| < 10^{-6}
3018 * \|\boldsymbol{b}\|.\f] This time the constraints are empty as we do not
3019 * use the Dirichlet boundary condition. Consequently, we do not have to
3020 * distribute the constraints.
3021 *
3022 * @code
3023 *   void Solver::solve()
3024 *   {
3025 *   SolverControl control(system_rhs.size(), 1.0e-6 * system_rhs.l2_norm());
3026 *  
3028 *   SolverCG<Vector<double>> cg(control, memory);
3029 *  
3030 *   PreconditionSSOR<SparseMatrix<double>> preconditioner;
3031 *   preconditioner.initialize(system_matrix, 1.2);
3032 *  
3033 *   cg.solve(system_matrix, solution_Hdiv, system_rhs, preconditioner);
3034 *   }
3035 *  
3036 * @endcode
3037 *
3038 * The following function saves the computed fields into a `.vtu` file.
3039 * This time we also save the projected exact solution and the @f$L^2@f$ error
3040 * norm. The exact solution is only saved if
3041 * `Settings::project_exact_solution = true`
3042 *
3043 * @code
3044 *   void Solver::output_results() const
3045 *   {
3046 *   std::vector<std::string> solution_names(3, "VectorField");
3047 *   std::vector<DataComponentInterpretation::DataComponentInterpretation>
3048 *   interpretation(3,
3050 *  
3051 *   DataOut<3> data_out;
3052 *  
3053 *   data_out.add_data_vector(dof_handler_Hdiv,
3054 *   solution_Hdiv,
3055 *   solution_names,
3056 *   interpretation);
3057 *  
3058 *   if (Settings::project_exact_solution)
3059 *   {
3060 *   std::vector<std::string> solution_names_ex(3, "VectorFieldExact");
3061 *  
3062 *   data_out.add_data_vector(dof_handler_Hdiv,
3063 *   projected_exact_solution,
3064 *   solution_names_ex,
3065 *   interpretation);
3066 *   }
3067 *  
3068 *   if (exact_solution)
3069 *   {
3070 *   data_out.add_data_vector(L2_per_cell, "L2norm");
3071 *   }
3072 *  
3073 *   DataOutBase::VtkFlags flags;
3074 *   flags.write_higher_order_cells = true;
3075 *   data_out.set_flags(flags);
3076 *  
3077 *   const MappingQ<3> mapping(mapping_degree);
3078 *  
3079 *   data_out.build_patches(mapping,
3080 *   fe_Hdiv.degree + 2,
3082 *  
3083 *   std::ofstream ofs(fname + ".vtu");
3084 *   data_out.write_vtu(ofs);
3085 *   }
3086 *  
3087 * @endcode
3088 *
3089 * The `run` function below aggregates all the computation steps in the
3090 * correct order.
3091 *
3092 * @code
3093 *   void Solver::run()
3094 *   {
3095 *   setup();
3096 *   assemble();
3097 *   solve();
3098 *   compute_error_norms();
3099 *   project_exact_solution_fcn();
3100 *   output_results();
3101 *   clear();
3102 *   }
3103 *   } // namespace ProjectorHcurlToHdiv
3104 *  
3105 *  
3106 * @endcode
3107 *
3108 *
3109 * <a name="step_97-Themainloop"></a>
3110 * <h3>The main loop</h3>
3111 *
3112
3113 *
3114 * The following class contains the main loop of the program.
3115 *
3116 * @code
3117 *   class MagneticProblem
3118 *   {
3119 *   public:
3120 *   void run()
3121 *   {
3122 *   if (Settings::n_threads_max)
3123 *   MultithreadInfo::set_thread_limit(Settings::n_threads_max);
3124 *  
3125 *   MainOutputTable table_Jf(3);
3126 *   MainOutputTable table_B(3);
3127 *  
3128 *   std::cout << "Solving for (p = " << Settings::fe_degree
3129 *   << "): " << std::flush;
3130 *  
3131 *   for (unsigned int r = 6; r < 10; r++) // Mesh refinement parameter.
3132 *   {
3133 *   table_Jf.add_value("r", r);
3134 *   table_Jf.add_value("p", Settings::fe_degree);
3135 *  
3136 *   table_B.add_value("r", r);
3137 *   table_B.add_value("p", Settings::fe_degree);
3138 *  
3139 * @endcode
3140 *
3141 * Stage 1. Computing @f$\vec{T}@f$.
3142 *
3143
3144 *
3145 *
3146 * @code
3147 *   std::cout << "T " << std::flush;
3148 *  
3149 *   SolverT::Solver T(Settings::fe_degree,
3150 *   r,
3151 *   Settings::mapping_degree,
3152 *   Settings::eta_squared_T,
3153 *   "T_p" + std::to_string(Settings::fe_degree) + "_r" +
3154 *   std::to_string(r));
3155 *  
3156 *   T.run();
3157 *  
3158 * @endcode
3159 *
3160 * Stage 2. Computing @f$\vec{J}_f@f$.
3161 *
3162
3163 *
3164 *
3165 * @code
3166 *   std::cout << "Jf " << std::flush;
3167 *  
3168 *   ExactSolutions::FreeCurrentDensity Jf_exact;
3169 *  
3170 *   ProjectorHcurlToHdiv::Solver Jf(Settings::fe_degree,
3171 *   Settings::mapping_degree,
3172 *   T.get_tria(),
3173 *   T.get_dof_handler(),
3174 *   T.get_solution(),
3175 *   "Jf_p" +
3176 *   std::to_string(Settings::fe_degree) +
3177 *   "_r" + std::to_string(r),
3178 *   &Jf_exact);
3179 *  
3180 *   Jf.run();
3181 *  
3182 *   table_Jf.add_value("ndofs", Jf.get_n_dofs());
3183 *   table_Jf.add_value("ncells", Jf.get_n_cells());
3184 *   table_Jf.add_value("L2", Jf.get_L2_norm());
3185 *  
3186 * @endcode
3187 *
3188 * Stage 3. Computing @f$\vec{A}@f$.
3189 *
3190
3191 *
3192 *
3193 * @code
3194 *   std::cout << "A " << std::flush;
3195 *  
3196 *   SolverA::Solver A(Settings::fe_degree,
3197 *   Settings::mapping_degree,
3198 *   T.get_tria(),
3199 *   T.get_dof_handler(),
3200 *   T.get_solution(),
3201 *   Settings::eta_squared_A,
3202 *   "A_p" + std::to_string(Settings::fe_degree) + "_r" +
3203 *   std::to_string(r));
3204 *  
3205 *   A.run();
3206 *  
3207 * @endcode
3208 *
3209 * Stage 4. Computing @f$\vec{B}@f$.
3210 *
3211
3212 *
3213 *
3214 * @code
3215 *   std::cout << "B " << std::flush;
3216 *  
3217 *   ExactSolutions::MagneticField B_exact;
3218 *  
3219 *   ProjectorHcurlToHdiv::Solver B(Settings::fe_degree,
3220 *   Settings::mapping_degree,
3221 *   T.get_tria(),
3222 *   A.get_dof_handler(),
3223 *   A.get_solution(),
3224 *   "B_p" +
3225 *   std::to_string(Settings::fe_degree) +
3226 *   "_r" + std::to_string(r),
3227 *   &B_exact);
3228 *   B.run();
3229 *  
3230 *   table_B.add_value("ndofs", B.get_n_dofs());
3231 *   table_B.add_value("ncells", B.get_n_cells());
3232 *   table_B.add_value("L2", B.get_L2_norm());
3233 * @endcode
3234 *
3235 * End stage 4.
3236 *
3237 * @code
3238 *   }
3239 *  
3240 *   table_Jf.save("table_Jf_p" + std::to_string(Settings::fe_degree));
3241 *   table_B.save("table_B_p" + std::to_string(Settings::fe_degree));
3242 *   std::cout << std::endl;
3243 *   }
3244 *   };
3245 *  
3246 *   int main()
3247 *   {
3248 *   try
3249 *   {
3250 *   MagneticProblem problem;
3251 *   problem.run();
3252 *   }
3253 *   catch (std::exception &exc)
3254 *   {
3255 *   std::cerr << std::endl
3256 *   << std::endl
3257 *   << "----------------------------------------------------"
3258 *   << std::endl;
3259 *   std::cerr << "Exception on processing: " << std::endl
3260 *   << exc.what() << std::endl
3261 *   << "Aborting!" << std::endl
3262 *   << "----------------------------------------------------"
3263 *   << std::endl;
3264 *  
3265 *   return 1;
3266 *   }
3267 *   catch (...)
3268 *   {
3269 *   std::cerr << std::endl
3270 *   << std::endl
3271 *   << "----------------------------------------------------"
3272 *   << std::endl;
3273 *   std::cerr << "Unknown exception!" << std::endl
3274 *   << "Aborting!" << std::endl
3275 *   << "----------------------------------------------------"
3276 *   << std::endl;
3277 *   return 1;
3278 *   }
3279 *  
3280 *   return 0;
3281 *   }
3282 * @endcode
3283<a name="step_97-Results"></a><h1>Results</h1>
3284
3285
3286The program generates the following output in the command line interface by
3287default.
3288
3289@code
3290Solving for (p = 0): T Jf A B T Jf A B T Jf A B T Jf A B
3291@endcode
3292
3293The program assumes the finite elements of the lowermost degree, @f$p = 0@f$.
3294To change the degree of the finite elements, say @f$p = 2@f$, one needs to change
3295the setting `Settings::fe_degree = 2` and rebuild the program.
3296
3297The program also dumps a number of files in the current directory. In the default
3298configuration these files are:
3299- `.vtu` files. They contain the computed vector fields. Recall that the spherical
3300 manifold is attached to many cell faces. Consequently, these cell faces are
3301 curved. They look more like patches of a sphere. Furthermore, the shape
3302 functions are mapped from the reference cell to the real mesh cells by the
3303 second-order mapping to accommodate the cells with curved faces. For these
3304 reasons, one needs to use a visualization software that can deal with curved
3305 faces and the higher-order mapping. A fresh version of ParaView is recommended.
3306 Visit will not do. The <a href="https://github.com/dealii/dealii/wiki/Notes-on-visualizing-high-order-output">
3307 Notes on visualizing high order output</a> provide more information on this topic.
3308- `.tex` files. These files contain the convergence tables.
3309
3310The following provides examples of the convergence tables simulated with the
3311default settings for three different degrees of the finite elements,
3312@f$p = 0, 1, 2@f$.
3313
3314<table>
3315<caption>Convergence table @f$\vec{J}_f@f$.</caption>
3316 <tr>
3317 <th>p</td>
3318 <th>r</td>
3319 <th>cells</td>
3320 <th>dofs</td>
3321 <th>@f$\|e\|_{L^2}@f$</td>
3322 <th>@f$\alpha_{L^2}@f$</td>
3323 </tr>
3324 <tr>
3325 <td>0</td>
3326 <td>6</td>
3327 <td>4625</td>
3328 <td>13950</td>
3329 <td>1.66e-01</td>
3330 <td>-</td>
3331 </tr>
3332 <tr>
3333 <td>0</td>
3334 <td>7</td>
3335 <td>7992</td>
3336 <td>24084</td>
3337 <td>1.38e-01</td>
3338 <td>0.99</td>
3339 </tr>
3340 <tr>
3341 <td>0</td>
3342 <td>8</td>
3343 <td>12691</td>
3344 <td>38220</td>
3345 <td>1.19e-01</td>
3346 <td>0.99</td>
3347 </tr>
3348 <tr>
3349 <td>0</td>
3350 <td>9</td>
3351 <td>18944</td>
3352 <td>57024</td>
3353 <td>1.04e-01</td>
3354 <td>0.99</td>
3355 </tr>
3356 <tr>
3357 <td>1</td>
3358 <td>6</td>
3359 <td>4625</td>
3360 <td>111300</td>
3361 <td>8.12e-04</td>
3362 <td>-</td>
3363 </tr>
3364 <tr>
3365 <td>1</td>
3366 <td>7</td>
3367 <td>7992</td>
3368 <td>192240</td>
3369 <td>4.97e-04</td>
3370 <td>2.69</td>
3371 </tr>
3372 <tr>
3373 <td>1</td>
3374 <td>8</td>
3375 <td>12691</td>
3376 <td>305172</td>
3377 <td>3.32e-04</td>
3378 <td>2.61</td>
3379 </tr>
3380 <tr>
3381 <td>1</td>
3382 <td>9</td>
3383 <td>18944</td>
3384 <td>455424</td>
3385 <td>2.37e-04</td>
3386 <td>2.54</td>
3387 </tr>
3388 <tr>
3389 <td>2</td>
3390 <td>6</td>
3391 <td>4625</td>
3392 <td>375300</td>
3393 <td>6.78e-04</td>
3394 <td>-</td>
3395 </tr>
3396 <tr>
3397 <td>2</td>
3398 <td>7</td>
3399 <td>7992</td>
3400 <td>648324</td>
3401 <td>3.94e-04</td>
3402 <td>2.97</td>
3403 </tr>
3404 <tr>
3405 <td>2</td>
3406 <td>8</td>
3407 <td>12691</td>
3408 <td>1029294</td>
3409 <td>2.49e-04</td>
3410 <td>2.98</td>
3411 </tr>
3412 <tr>
3413 <td>2</td>
3414 <td>9</td>
3415 <td>18944</td>
3416 <td>1536192</td>
3417 <td>1.67e-04</td>
3418 <td>2.99</td>
3419 </tr>
3420</table>
3421<br>
3422<table>
3423<caption>Convergence table @f$\vec{B}@f$.</caption>
3424 <tr>
3425 <th>p</td>
3426 <th>r</td>
3427 <th>cells</td>
3428 <th>dofs</td>
3429 <th>@f$\|e\|_{L^2}@f$</td>
3430 <th>@f$\alpha_{L^2}@f$</td>
3431 </tr>
3432 <tr>
3433 <td>0</td>
3434 <td>6</td>
3435 <td>4625</td>
3436 <td>13950</td>
3437 <td>8.84e-08</td>
3438 <td>-</td>
3439 </tr>
3440 <tr>
3441 <td>0</td>
3442 <td>7</td>
3443 <td>7992</td>
3444 <td>24084</td>
3445 <td>7.36e-08</td>
3446 <td>1.00</td>
3447 </tr>
3448 <tr>
3449 <td>0</td>
3450 <td>8</td>
3451 <td>12691</td>
3452 <td>38220</td>
3453 <td>6.30e-08</td>
3454 <td>1.01</td>
3455 </tr>
3456 <tr>
3457 <td>0</td>
3458 <td>9</td>
3459 <td>18944</td>
3460 <td>57024</td>
3461 <td>5.51e-08</td>
3462 <td>1.00</td>
3463 </tr>
3464 <tr>
3465 <td>1</td>
3466 <td>6</td>
3467 <td>4625</td>
3468 <td>111300</td>
3469 <td>4.41e-09</td>
3470 <td>-</td>
3471 </tr>
3472 <tr>
3473 <td>1</td>
3474 <td>7</td>
3475 <td>7992</td>
3476 <td>192240</td>
3477 <td>3.11e-09</td>
3478 <td>1.91</td>
3479 </tr>
3480 <tr>
3481 <td>1</td>
3482 <td>8</td>
3483 <td>12691</td>
3484 <td>305172</td>
3485 <td>2.23e-09</td>
3486 <td>2.18</td>
3487 </tr>
3488 <tr>
3489 <td>1</td>
3490 <td>9</td>
3491 <td>18944</td>
3492 <td>455424</td>
3493 <td>1.71e-09</td>
3494 <td>1.96</td>
3495 </tr>
3496 <tr>
3497 <td>2</td>
3498 <td>6</td>
3499 <td>4625</td>
3500 <td>375300</td>
3501 <td>1.84e-10</td>
3502 <td>-</td>
3503 </tr>
3504 <tr>
3505 <td>2</td>
3506 <td>7</td>
3507 <td>7992</td>
3508 <td>648324</td>
3509 <td>1.03e-10</td>
3510 <td>3.21</td>
3511 </tr>
3512 <tr>
3513 <td>2</td>
3514 <td>8</td>
3515 <td>12691</td>
3516 <td>1029294</td>
3517 <td>6.08e-11</td>
3518 <td>3.40</td>
3519 </tr>
3520 <tr>
3521 <td>2</td>
3522 <td>9</td>
3523 <td>18944</td>
3524 <td>1536192</td>
3525 <td>4.04e-11</td>
3526 <td>3.07</td>
3527 </tr>
3528</table>
3529
3530The following notations were used in the headers of the tables:
3531
3532- p - the degree of the finite elements.
3533
3534- r - the mesh refinement parameter, i.e., the number of nodes on the transfinite
3535lines.
3536
3537- cells - the total amount of active cells.
3538
3539- dofs - the amount of degrees of freedom.
3540
3541-@f$\|e\|_{L^2}@f$ - the @f$L^2@f$ error norm.
3542
3543-@f$\alpha_{L^2}@f$ - the order of convergence of the @f$L^2@f$ error norm.
3544
3545The vector representations of the calculated vector fields, @f$\vec{J}_f@f$ and
3546@f$\vec{B}@f$, are illustrated above by the first figure on this page. The figures
3547below illustrate slices of the magnitudes of these fields. The figures below
3548were simulated with @f$p = 2@f$ and @f$r = 9@f$. Visual inspection of the vector
3549potentials is not very informative as their conservative portions are unknown.
3550
3551@htmlonly
3552<p align="center">
3553 <img src="https://dealii.org/images/steps/developer/step-97-Jf.svg" alt="The result - free-current
3554 density" height="531">
3555</p>
3556@endhtmlonly
3557
3558@htmlonly
3559<p align="center">
3560 <img src="https://dealii.org/images/steps/developer/step-97-B.svg" alt="The result - magnetic
3561 field" height="531">
3562</p>
3563@endhtmlonly
3564
3565@anchor Step97_PossibilitiesForExtensions
3566<a name="step_97-Possibilitiesforextensions"></a><h3>Possibilities for extensions</h3>
3567
3568
3569Repeat the simulations for the three types of the boundary conditions,
3570Dirichlet, Neumann, and Robin. The Robin boundary condition is supposed to be
3571superior to the other two. Look at the simulated data to see that this is indeed
3572the case. You can save the projected exact solution next to the simulated
3573solutions into the `.vtu` files, just set `Settings::project_exact_solution = true`.
3574ParaView has "Plot Over Line" filter. You can use this filter to visualize the
3575difference between the exact solution and a solution simulated with a particular
3576boundary condition. You can also draw conclusions by observing the convergence
3577tables. Keep in mind the @f$\eta^2@f$ parameter. Increase it if the CG solver chokes
3578while you are experimenting. Note that the benefits offered by the Robin
3579boundary condition are observed the best when higher-order finite elements are
3580used, i.e., @f$p = 1@f$ and @f$p = 2@f$.
3581
3582The Robin boundary condition as described above is also called the first-order
3583asymptotic boundary condition (ABC). There exist ABCs of higher orders
3584@cite gratkowski2010p. Implement and test the second-order ABC to see if it
3585performs any better. There exist improvised asymptotic boundary conditions, IABCs,
3586@cite meeker2013a. Try to implement the first order IABC.
3587 *
3588 *
3589<a name="step_97-PlainProg"></a>
3590<h1> The plain program</h1>
3591@include "step-97.cc"
3592*/
*  iterator end()
*  const Number height
*  *  for(const auto &cell :triangulation.active_cell_iterators())
*  *  int main(int argc, char **argv)
*  *  iterator begin()
*  x_component_mask set(0, true)
*  *  *  struct InterferenceTaperTransform *  
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={})
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())
constexpr void clear()
Point< 2 > second
Definition grid_out.cc:4640
Point< 2 > first
Definition grid_out.cc:4639
#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 project_boundary_values_curl_conforming_l2(const DoFHandler< dim, dim > &dof_handler, const unsigned int first_vector_component, const Function< dim, number > &boundary_function, const types::boundary_id boundary_component, AffineConstraints< number > &constraints, const Mapping< dim > &mapping)
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)
@ update_values
Shape function values.
@ update_normal_vectors
Normal vectors.
@ update_JxW_values
Transformed quadrature weights.
@ update_gradients
Shape function gradients.
@ update_quadrature_points
Transformed quadrature points.
std::vector< index_type > data
Definition mpi.cc:734
double volume(const Triangulation< dim, spacedim > &tria)
@ 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
SymmetricTensor< 2, dim, Number > e(const Tensor< 2, dim, Number > &F)
SymmetricTensor< 2, dim, Number > b(const Tensor< 2, dim, Number > &F)
*  *  if(update_pressure &update_flags) *  compute_pressure(constitutive_request
*  *  *  *  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)
constexpr ReturnType< rank, T >::value_type & extract(T &t, const ArrayType &indices)
double compute_global_error(const Triangulation< dim, spacedim > &tria, const InVector &cellwise_error, const NormType &norm, const double exponent=2.)
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 save(Archive &ar, const ::std_cxx26::inplace_vector< T, N > &vec, const unsigned int)
long double gamma(const unsigned int n)
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
STL namespace.
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)
Definition types.h:30
unsigned int material_id
Definition types.h:182