1558 * The following three get-
functions are used to channel the mesh and the
1559 * solution to the next solver.
1564 *
return triangulation;
1568 *
return dof_handler;
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.
1601 *
const unsigned int refinement_parameter;
1602 *
const unsigned int mapping_degree;
1603 *
const double eta_squared;
1604 *
const std::string fname;
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.
1614 *
struct AssemblyScratchData
1617 *
const double eta_squared,
1618 *
const unsigned int mapping_degree);
1620 *
AssemblyScratchData(
const AssemblyScratchData &scratch_data);
1622 *
const FreeCurrentDensity Jf;
1627 *
const unsigned int dofs_per_cell;
1628 *
const unsigned int n_q_points;
1630 *
std::vector<Tensor<1, 3>> Jf_list;
1632 *
const double eta_squared;
1635 *
struct AssemblyCopyData
1639 *
std::vector<types::global_dof_index> local_dof_indices;
1642 *
void system_matrix_local(
1644 *
AssemblyScratchData &scratch_data,
1645 *
AssemblyCopyData ©_data);
1647 *
void copy_local_to_global(
const AssemblyCopyData ©_data);
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)
1656 *
, refinement_parameter(r)
1657 *
, mapping_degree(mapping_degree)
1658 *
, eta_squared(eta_squared)
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
1673 *
void Solver::make_mesh()
1678 *
std::ifstream ifs(
"sphere_r" + std::to_string(refinement_parameter) +
1680 *
gridin.read_msh(ifs);
1682 *
triangulation.reset_all_manifolds();
1684 *
for (
auto cell : triangulation.active_cell_iterators())
1686 *
cell->set_material_id(
1687 *
Settings::material_id_free_space);
1689 *
if ((cell->center().norm() > Settings::a1) &&
1690 *
(cell->center().norm() < Settings::b1))
1691 *
cell->set_material_id(
1692 *
Settings::material_id_core);
1694 *
if ((cell->center().norm() > Settings::a2) &&
1695 *
(cell->center().norm() < Settings::b2))
1696 *
cell->set_material_id(
1697 *
Settings::material_id_free_current);
1700 *
for (
unsigned int f = 0; f < cell->n_faces(); f++)
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());
1707 *
if ((dif_norm < Settings::eps) &&
1708 *
(cell->center().norm() > Settings::d1))
1709 *
cell->face(f)->set_all_manifold_ids(1);
1713 *
triangulation.set_manifold(1, sphere);
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
1730 *
void Solver::setup()
1732 *
dof_handler.reinit(triangulation);
1733 *
dof_handler.distribute_dofs(fe);
1735 *
constraints.clear();
1743 *
Settings::boundary_id_infinity,
1747 *
constraints.close();
1751 *
sparsity_pattern.copy_from(dsp);
1753 *
system_matrix.reinit(sparsity_pattern);
1754 *
solution.reinit(dof_handler.n_dofs());
1755 *
system_rhs.reinit(dof_handler.n_dofs());
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()`.
1767 *
void Solver::assemble()
1770 *
dof_handler.end(),
1772 *
&Solver::system_matrix_local,
1773 *
&Solver::copy_local_to_global,
1774 *
AssemblyScratchData(fe, eta_squared, mapping_degree),
1775 *
AssemblyCopyData());
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
1785 *
Solver::AssemblyScratchData::AssemblyScratchData(
1787 *
const double eta_squared,
1788 *
const unsigned int mapping_degree)
1790 *
, mapping(mapping_degree)
1791 *
, fe_values(mapping,
1796 *
, dofs_per_cell(fe_values.dofs_per_cell)
1797 *
, n_q_points(fe_values.get_quadrature().size())
1799 *
, eta_squared(eta_squared)
1802 *
Solver::AssemblyScratchData::AssemblyScratchData(
1803 *
const AssemblyScratchData &scratch_data)
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())
1814 *
, eta_squared(scratch_data.eta_squared)
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()`.
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
1841 *
void Solver::system_matrix_local(
1843 *
AssemblyScratchData &scratch_data,
1844 *
AssemblyCopyData ©_data)
1846 *
copy_data.cell_matrix.reinit(scratch_data.dofs_per_cell,
1847 *
scratch_data.dofs_per_cell);
1849 *
copy_data.cell_rhs.reinit(scratch_data.dofs_per_cell);
1851 *
copy_data.local_dof_indices.resize(scratch_data.dofs_per_cell);
1853 *
scratch_data.fe_values.reinit(cell);
1855 *
scratch_data.Jf.value_list(scratch_data.fe_values.get_quadrature_points(),
1856 *
cell->material_id(),
1857 *
scratch_data.Jf_list);
1861 *
for (
unsigned int q_index = 0; q_index < scratch_data.n_q_points; ++q_index)
1863 *
for (
unsigned int i = 0; i < scratch_data.dofs_per_cell; ++i)
1865 *
for (
unsigned int j = 0; j < scratch_data.dofs_per_cell; ++j)
1867 *
copy_data.cell_matrix(i, j) +=
1868 *
(scratch_data.fe_values[ve].curl(i,
1870 *
scratch_data.fe_values[ve].curl(j,
1873 *
scratch_data.eta_squared *
1874 *
scratch_data.fe_values[ve].value(i,
1876 *
scratch_data.fe_values[ve].value(j, q_index)
1878 *
scratch_data.fe_values.JxW(q_index);
1880 *
copy_data.cell_rhs(i) +=
1881 *
(scratch_data.Jf_list[q_index] *
1882 *
scratch_data.fe_values[ve].curl(i, q_index)) *
1883 *
scratch_data.fe_values.JxW(
1888 *
cell->get_dof_indices(copy_data.local_dof_indices);
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
1898 *
void Solver::copy_local_to_global(
const AssemblyCopyData ©_data)
1900 *
constraints.distribute_local_to_global(copy_data.cell_matrix,
1901 *
copy_data.cell_rhs,
1902 *
copy_data.local_dof_indices,
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
1918 *
void Solver::solve()
1920 *
SolverControl control(system_rhs.size(), 1.0e-6 * system_rhs.l2_norm());
1926 *
preconditioner.
initialize(system_matrix, 1.2);
1928 *
cg.solve(system_matrix, solution, system_rhs, preconditioner);
1930 *
constraints.distribute(solution);
1935 * The following function saves the computed current vector potential into a
1939 *
void Solver::output_results() const
1941 *
const std::vector<std::string> solution_names(3,
"VectorField");
1942 *
const std::vector<DataComponentInterpretation::DataComponentInterpretation>
1955 *
data_out.set_flags(flags);
1959 *
data_out.build_patches(mapping,
1963 *
std::ofstream ofs(fname +
".vtu");
1964 *
data_out.write_vtu(ofs);
1969 * The `
run` function below aggregates all the computation steps in the
1973 *
void Solver::run()
1987 * <a name=
"step_97-SolverA"></a>
1988 * <h3>Solver -
A</h3>
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$.
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.
2010 *
class Permeability
2016 *
if ((mid ==
Settings::material_id_free_space) ||
2017 *
(mid ==
Settings::material_id_free_current))
2020 *
if (mid == Settings::material_id_core)
2021 *
std::fill(
values.begin(),
values.end(), Settings::mu_1);
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
2031 * \
gamma = \dfrac{1}{\mu_0 r}.
2038 *
void value_list(
const std::vector<
Point<3>> &r,
2039 *
std::vector<double> &values)
const
2042 *
ExcDimensionMismatch(r.size(),
values.size()));
2044 *
for (
unsigned int i = 0; i <
values.size(); i++)
2045 *
values[i] = 1.0 / (Settings::mu_0 * r[i].
norm());
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`.
2062 *
Solver() =
delete;
2063 *
Solver(
const unsigned int p,
2064 *
const unsigned int mapping_degree,
2068 *
const double eta_squared = 0.0,
2069 *
const std::string &fname =
"data");
2074 *
void output_results()
const;
2077 *
system_matrix.clear();
2078 *
system_rhs.reinit(0);
2085 *
return dof_handler;
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.
2115 *
const unsigned int mapping_degree;
2116 *
const double eta_squared;
2117 *
const std::string fname;
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.
2128 *
using IteratorTuple =
2129 *
std::tuple<typename DoFHandler<3>::active_cell_iterator,
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.
2142 *
struct AssemblyScratchData
2147 *
const unsigned int mapping_degree,
2148 *
const double eta_squared,
2149 *
const BoundaryConditionType boundary_condition_type);
2151 *
AssemblyScratchData(
const AssemblyScratchData &scratch_data);
2153 *
const Permeability permeability;
2154 *
const Gamma
gamma;
2162 *
const unsigned int dofs_per_cell;
2163 *
const unsigned int n_q_points;
2164 *
const unsigned int n_q_points_face;
2166 *
std::vector<double> permeability_list;
2167 *
std::vector<double> gamma_list;
2168 *
std::vector<Tensor<1, 3>> T_values;
2173 *
const double eta_squared;
2174 *
const BoundaryConditionType boundary_condition_type;
2177 *
struct AssemblyCopyData
2181 *
std::vector<types::global_dof_index> local_dof_indices;
2184 *
void system_matrix_local(
const IteratorPair &IP,
2185 *
AssemblyScratchData &scratch_data,
2186 *
AssemblyCopyData ©_data);
2188 *
void copy_local_to_global(
const AssemblyCopyData ©_data);
2191 *
Solver::Solver(
const unsigned int p,
2192 *
const unsigned int mapping_degree,
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)
2202 *
, mapping_degree(mapping_degree)
2203 *
, eta_squared(eta_squared)
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
2225 *
void Solver::setup()
2227 *
dof_handler.reinit(triangulation_T);
2228 *
dof_handler.distribute_dofs(fe);
2230 *
constraints.clear();
2234 *
if (Settings::boundary_condition_type_A == Dirichlet)
2239 *
Settings::boundary_id_infinity,
2243 *
constraints.close();
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());
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
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.
2267 * void Solver::assemble()
2269 * WorkStream::run(IteratorPair({dof_handler.begin_active(),
2270 * dof_handler_T.begin_active()}),
2271 * IteratorPair({dof_handler.end(), dof_handler_T.end()}),
2273 * &Solver::system_matrix_local,
2274 * &Solver::copy_local_to_global,
2275 * AssemblyScratchData(fe,
2280 * Settings::boundary_condition_type_A),
2281 * AssemblyCopyData());
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
2291 *
Solver::AssemblyScratchData::AssemblyScratchData(
2295 *
const unsigned int mapping_degree,
2296 *
const double eta_squared,
2297 *
const BoundaryConditionType boundary_condition_type)
2300 *
, mapping(mapping_degree)
2301 *
, fe_values(mapping,
2305 *
, fe_face_values(mapping,
2310 *
, fe_values_T(mapping,
2311 *
dof_hand_T.get_fe(),
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)
2322 *
, eta_squared(eta_squared)
2323 *
, boundary_condition_type(boundary_condition_type)
2326 *
Solver::AssemblyScratchData::AssemblyScratchData(
2327 *
const AssemblyScratchData &scratch_data)
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)
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()`.
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.
2390 *
void Solver::system_matrix_local(
const IteratorPair &IP,
2391 *
AssemblyScratchData &scratch_data,
2392 *
AssemblyCopyData ©_data)
2394 *
copy_data.cell_matrix.reinit(scratch_data.dofs_per_cell,
2395 *
scratch_data.dofs_per_cell);
2397 *
copy_data.cell_rhs.reinit(scratch_data.dofs_per_cell);
2399 *
copy_data.local_dof_indices.resize(scratch_data.dofs_per_cell);
2405 *
scratch_data.fe_values.reinit(cell);
2406 *
scratch_data.fe_values_T.reinit(cell_T);
2408 *
scratch_data.permeability.value_list(cell->material_id(),
2409 *
scratch_data.permeability_list);
2413 *
scratch_data.fe_values_T[ve].get_function_values(scratch_data.dofs_T,
2414 *
scratch_data.T_values);
2416 *
for (
unsigned int q_index = 0; q_index < scratch_data.n_q_points; ++q_index)
2418 *
for (
unsigned int i = 0; i < scratch_data.dofs_per_cell; ++i)
2420 *
for (
unsigned int j = 0; j < scratch_data.dofs_per_cell; ++j)
2422 *
copy_data.cell_matrix(i, j) +=
2423 *
(1.0 / scratch_data.permeability_list[q_index]) *
2424 *
(scratch_data.fe_values[ve].curl(i,
2426 *
scratch_data.fe_values[ve].curl(j,
2429 *
scratch_data.eta_squared *
2430 *
scratch_data.fe_values[ve].value(i,
2432 *
scratch_data.fe_values[ve].value(j, q_index)
2434 *
scratch_data.fe_values.JxW(q_index);
2436 *
copy_data.cell_rhs(i) +=
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);
2443 *
if (scratch_data.boundary_condition_type == BoundaryConditionType::Robin)
2445 *
for (
unsigned int f = 0; f < cell->n_faces(); ++f)
2447 *
if (cell->face(f)->at_boundary())
2449 *
scratch_data.fe_face_values.reinit(cell, f);
2451 *
for (
unsigned int q_index_face = 0;
2452 *
q_index_face < scratch_data.n_q_points_face;
2455 *
for (
unsigned int i = 0; i < scratch_data.dofs_per_cell;
2458 *
scratch_data.gamma.value_list(
2459 *
scratch_data.fe_face_values.get_quadrature_points(),
2460 *
scratch_data.gamma_list);
2462 *
for (
unsigned int j = 0; j < scratch_data.dofs_per_cell;
2465 *
copy_data.cell_matrix(i, j) +=
2466 *
scratch_data.gamma_list[q_index_face] *
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)))
2476 *
* scratch_data.fe_face_values.JxW(
2486 *
cell->get_dof_indices(copy_data.local_dof_indices);
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,
2496 *
void Solver::copy_local_to_global(
const AssemblyCopyData ©_data)
2498 *
constraints.distribute_local_to_global(copy_data.cell_matrix,
2499 *
copy_data.cell_rhs,
2500 *
copy_data.local_dof_indices,
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
2516 *
void Solver::solve()
2518 *
SolverControl control(system_rhs.size(), 1.0e-6 * system_rhs.l2_norm());
2524 *
preconditioner.
initialize(system_matrix, 1.2);
2526 *
cg.solve(system_matrix, solution, system_rhs, preconditioner);
2528 *
constraints.distribute(solution);
2533 * The following function saves the computed magnetic vector potential into a
2537 *
void Solver::output_results() const
2539 *
std::vector<std::string> solution_names(3,
"VectorField");
2540 *
std::vector<DataComponentInterpretation::DataComponentInterpretation>
2553 *
data_out.set_flags(flags);
2557 *
data_out.build_patches(mapping,
2561 *
std::ofstream ofs(fname +
".vtu");
2562 *
data_out.write_vtu(ofs);
2567 * The `
run` function below aggregates all the computation steps in the
2571 *
void Solver::run()
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$.
2595 *
namespace ProjectorHcurlToHdiv
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.
2613 *
Solver() =
delete;
2614 *
Solver(
const unsigned int p,
2615 *
const unsigned int mapping_degree,
2619 *
const std::string &fname =
"data",
2622 *
double get_L2_norm()
2627 *
unsigned int get_n_cells() const
2629 *
return triangulation_Hcurl.n_active_cells();
2634 *
return dof_handler_Hdiv.n_dofs();
2640 *
void output_results()
const;
2641 *
void compute_error_norms();
2642 *
void project_exact_solution_fcn();
2645 *
system_matrix.clear();
2646 *
system_rhs.reinit(0);
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.
2679 *
const unsigned int mapping_degree;
2684 *
const std::string fname;
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.
2696 *
using IteratorTuple =
2697 *
std::tuple<typename DoFHandler<3>::active_cell_iterator,
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.
2710 *
struct AssemblyScratchData
2715 *
const unsigned int mapping_degree);
2717 *
AssemblyScratchData(
const AssemblyScratchData &scratch_data);
2723 *
const unsigned int dofs_per_cell;
2724 *
const unsigned int n_q_points;
2726 *
std::vector<Tensor<1, 3>> curl_vec_in_Hcurl;
2732 *
struct AssemblyCopyData
2736 *
std::vector<types::global_dof_index> local_dof_indices;
2739 *
void system_matrix_local(
const IteratorPair &IP,
2740 *
AssemblyScratchData &scratch_data,
2741 *
AssemblyCopyData ©_data);
2743 *
void copy_local_to_global(
const AssemblyCopyData ©_data);
2746 *
Solver::Solver(
const unsigned int p,
2747 *
const unsigned int mapping_degree,
2751 *
const std::string &fname,
2753 *
: triangulation_Hcurl(triangulation_Hcurl)
2754 *
, dof_handler_Hcurl(dof_handler_Hcurl)
2755 *
, solution_Hcurl(solution_Hcurl)
2757 *
, exact_solution(exact_solution)
2758 *
, mapping_degree(mapping_degree)
2761 *
Assert(exact_solution !=
nullptr,
2762 *
ExcMessage(
"The exact solution is missing."));
2767 * The following function initializes the dofs, vectors and matrices. This
2768 * time there are no constraints as we
do not
apply Dirichlet boundary
2772 *
void Solver::setup()
2774 *
constraints.close();
2776 *
dof_handler_Hdiv.reinit(triangulation_Hcurl);
2777 *
dof_handler_Hdiv.distribute_dofs(fe_Hdiv);
2780 *
dof_handler_Hdiv.n_dofs());
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());
2788 *
if (Settings::project_exact_solution && exact_solution)
2789 *
projected_exact_solution.reinit(dof_handler_Hdiv.n_dofs());
2791 *
if (exact_solution)
2792 *
L2_per_cell.reinit(triangulation_Hcurl.n_active_cells());
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
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.
2808 * void Solver::assemble()
2810 * WorkStream::run(IteratorPair({dof_handler_Hdiv.begin_active(),
2811 * dof_handler_Hcurl.begin_active()}),
2813 * {dof_handler_Hdiv.end(), dof_handler_Hcurl.end()}),
2815 * &Solver::system_matrix_local,
2816 * &Solver::copy_local_to_global,
2817 * AssemblyScratchData(fe_Hdiv,
2818 * dof_handler_Hcurl,
2821 * AssemblyCopyData());
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
2831 *
Solver::AssemblyScratchData::AssemblyScratchData(
2835 *
const unsigned int mapping_degree)
2836 *
: mapping(mapping_degree)
2837 *
, fe_values_Hdiv(mapping,
2841 *
, fe_values_Hcurl(mapping,
2842 *
dof_hand_Hcurl.get_fe(),
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)
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)
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()`.
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
2895 *
void Solver::system_matrix_local(
const IteratorPair &IP,
2896 *
AssemblyScratchData &scratch_data,
2897 *
AssemblyCopyData ©_data)
2899 *
copy_data.cell_matrix.reinit(scratch_data.dofs_per_cell,
2900 *
scratch_data.dofs_per_cell);
2902 *
copy_data.cell_rhs.reinit(scratch_data.dofs_per_cell);
2904 *
copy_data.local_dof_indices.resize(scratch_data.dofs_per_cell);
2911 *
scratch_data.fe_values_Hdiv.reinit(cell_Hdiv);
2912 *
scratch_data.fe_values_Hcurl.reinit(cell_Hcurl);
2916 *
scratch_data.fe_values_Hcurl[ve].get_function_curls(
2917 *
scratch_data.dofs_Hcurl, scratch_data.curl_vec_in_Hcurl);
2919 *
for (
unsigned int q_index = 0; q_index < scratch_data.n_q_points; ++q_index)
2921 *
for (
unsigned int i = 0; i < scratch_data.dofs_per_cell; ++i)
2923 *
for (
unsigned int j = 0; j < scratch_data.dofs_per_cell; ++j)
2925 *
copy_data.cell_matrix(i, j) +=
2926 *
scratch_data.fe_values_Hdiv[ve].value(i,
2928 *
scratch_data.fe_values_Hdiv[ve].value(j,
2930 *
scratch_data.fe_values_Hdiv.JxW(q_index);
2933 *
copy_data.cell_rhs(i) +=
2935 *
.curl_vec_in_Hcurl[q_index] *
2937 *
scratch_data.fe_values_Hdiv[ve].value(i, q_index) *
2938 *
scratch_data.fe_values_Hdiv.JxW(q_index);
2942 *
cell_Hdiv->get_dof_indices(copy_data.local_dof_indices);
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
2952 *
void Solver::copy_local_to_global(
const AssemblyCopyData ©_data)
2954 *
constraints.distribute_local_to_global(copy_data.cell_matrix,
2955 *
copy_data.cell_rhs,
2956 *
copy_data.local_dof_indices,
2963 * The following two
functions compute the error norms and
project the exact
2967 *
void Solver::compute_error_norms()
2969 *
if (exact_solution)
2971 *
const Weight weight;
2989 *
void Solver::project_exact_solution_fcn()
2991 *
if (Settings::project_exact_solution && exact_solution)
2995 *
constraints_empty.
clear();
2998 *
constraints_empty);
3000 *
constraints_empty.close();
3004 *
constraints_empty,
3007 *
projected_exact_solution);
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.
3023 *
void Solver::solve()
3025 *
SolverControl control(system_rhs.size(), 1.0e-6 * system_rhs.l2_norm());
3031 *
preconditioner.
initialize(system_matrix, 1.2);
3033 *
cg.solve(system_matrix, solution_Hdiv, system_rhs, preconditioner);
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`
3044 *
void Solver::output_results() const
3046 *
std::vector<std::string> solution_names(3,
"VectorField");
3047 *
std::vector<DataComponentInterpretation::DataComponentInterpretation>
3058 *
if (Settings::project_exact_solution)
3060 *
std::vector<std::string> solution_names_ex(3,
"VectorFieldExact");
3062 *
data_out.add_data_vector(dof_handler_Hdiv,
3063 *
projected_exact_solution,
3064 *
solution_names_ex,
3068 *
if (exact_solution)
3070 *
data_out.add_data_vector(L2_per_cell,
"L2norm");
3075 *
data_out.set_flags(flags);
3079 *
data_out.build_patches(mapping,
3080 *
fe_Hdiv.degree + 2,
3083 *
std::ofstream ofs(fname +
".vtu");
3084 *
data_out.write_vtu(ofs);
3089 * The `
run` function below aggregates all the computation steps in the
3093 *
void Solver::run()
3098 *
compute_error_norms();
3099 *
project_exact_solution_fcn();
3109 * <a name=
"step_97-Themainloop"></a>
3114 * The following
class contains the
main loop of the program.
3117 *
class MagneticProblem
3122 *
if (Settings::n_threads_max)
3125 *
MainOutputTable table_Jf(3);
3126 *
MainOutputTable table_B(3);
3128 *
std::cout <<
"Solving for (p = " << Settings::fe_degree
3129 *
<<
"): " << std::flush;
3131 *
for (
unsigned int r = 6; r < 10; r++)
3133 *
table_Jf.add_value(
"r", r);
3134 *
table_Jf.add_value(
"p", Settings::fe_degree);
3136 *
table_B.add_value(
"r", r);
3137 *
table_B.add_value(
"p", Settings::fe_degree);
3141 * Stage 1. Computing @f$\vec{
T}@f$.
3147 *
std::cout <<
"T " << std::flush;
3149 *
SolverT::Solver
T(Settings::fe_degree,
3151 *
Settings::mapping_degree,
3152 *
Settings::eta_squared_T,
3153 *
"T_p" + std::to_string(Settings::fe_degree) +
"_r" +
3154 *
std::to_string(r));
3160 * Stage 2. Computing @f$\vec{
J}_f@f$.
3166 *
std::cout <<
"Jf " << std::flush;
3168 *
ExactSolutions::FreeCurrentDensity Jf_exact;
3170 *
ProjectorHcurlToHdiv::Solver Jf(Settings::fe_degree,
3171 *
Settings::mapping_degree,
3173 *
T.get_dof_handler(),
3176 *
std::to_string(Settings::fe_degree) +
3177 *
"_r" + std::to_string(r),
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());
3188 * Stage 3. Computing @f$\vec{
A}@f$.
3194 *
std::cout <<
"A " << std::flush;
3196 *
SolverA::Solver
A(Settings::fe_degree,
3197 *
Settings::mapping_degree,
3199 *
T.get_dof_handler(),
3201 *
Settings::eta_squared_A,
3202 *
"A_p" + std::to_string(Settings::fe_degree) +
"_r" +
3203 *
std::to_string(r));
3209 * Stage 4. Computing @f$\vec{B}@f$.
3215 *
std::cout <<
"B " << std::flush;
3217 *
ExactSolutions::MagneticField B_exact;
3219 *
ProjectorHcurlToHdiv::Solver B(Settings::fe_degree,
3220 *
Settings::mapping_degree,
3222 *
A.get_dof_handler(),
3225 *
std::to_string(Settings::fe_degree) +
3226 *
"_r" + std::to_string(r),
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());
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;
3250 *
MagneticProblem problem;
3253 *
catch (std::exception &exc)
3255 *
std::cerr << std::endl
3257 *
<<
"----------------------------------------------------"
3259 *
std::cerr <<
"Exception on processing: " << std::endl
3260 *
<< exc.what() << std::endl
3261 *
<<
"Aborting!" << std::endl
3262 *
<<
"----------------------------------------------------"
3269 *
std::cerr << std::endl
3271 *
<<
"----------------------------------------------------"
3273 *
std::cerr <<
"Unknown exception!" << std::endl
3274 *
<<
"Aborting!" << std::endl
3275 *
<<
"----------------------------------------------------"
3283<a name=
"step_97-Results"></a><h1>Results</h1>
3286The program generates the following output in the command line
interface by
3290Solving
for (p = 0):
T Jf
A B
T Jf
A B
T Jf
A B
T Jf
A B
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.
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
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.
3310The following provides examples of the convergence tables simulated with the
3311default settings
for three different degrees of the finite elements,
3315<caption>Convergence table @f$\vec{
J}_f@f$.</caption>
3321 <th>@f$\|
e\|_{
L^2}@f$</td>
3322 <th>@f$\alpha_{
L^2}@f$</td>
3423<caption>Convergence table @f$\vec{B}@f$.</caption>
3429 <th>@f$\|
e\|_{
L^2}@f$</td>
3430 <th>@f$\alpha_{
L^2}@f$</td>
3530The following notations were used in the headers of the tables:
3532- p - the degree of the finite elements.
3534- r - the mesh refinement parameter, i.
e., the number of nodes on the transfinite
3537- cells - the total amount of active cells.
3539- dofs - the amount of degrees of freedom.
3541-@f$\|
e\|_{
L^2}@f$ - the @f$L^2@f$ error
norm.
3543-@f$\alpha_{
L^2}@f$ - the order of convergence of the @f$L^2@f$ error
norm.
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.
3553 <img src=
"https://dealii.org/images/steps/developer/step-97-Jf.svg" alt=
"The result - free-current
3560 <img src=
"https://dealii.org/images/steps/developer/step-97-B.svg" alt=
"The result - magnetic
3565@anchor Step97_PossibilitiesForExtensions
3566<a name=
"step_97-Possibilitiesforextensions"></a><h3>Possibilities
for extensions</h3>
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$.
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.
3589<a name=
"step_97-PlainProg"></a>
3590<h1> The plain program</h1>
3591@include
"step-97.cc"
* * for(const auto &cell :triangulation.active_cell_iterators())
* * int main(int argc, char **argv)
* 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)
static void set_thread_limit(const unsigned int max_threads=numbers::invalid_unsigned_int)
void initialize(const MatrixType &A, const AdditionalData ¶meters=AdditionalData())
#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())
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
@ component_is_part_of_vector
@ matrix
Contents is actually a matrix.
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.)
double norm(const FEValuesBase< dim > &fe, const ArrayView< const std::vector< Tensor< 1, dim > > > &Du)
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)
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)
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)
bool write_higher_order_cells