659 *
const unsigned int component = 0)
const override
662 *
Assert(component == 0, ExcIndexRange(component, 0, 1));
721 *
if ((this->
get_time() <= 0.5) && (p[0] < 0) && (p[1] < 1. / 3) &&
741 *
if ((this->
get_time() <= 0.5) && (p[0] < 0) && (p[1] < 1. / 3) &&
754 * <a name=
"ImplementationofthecodeWaveEquationcodeclass"></a>
768 *
Let's start with the constructor (for an explanation of the choice of
769 * time step, see the section on Courant, Friedrichs, and Lewy in the
774 * WaveEquation<dim>::WaveEquation()
776 * , dof_handler(triangulation)
777 * , time_step(1. / 64)
779 * , timestep_number(1)
787 * <a name="WaveEquationsetup_system"></a>
788 * <h4>WaveEquation::setup_system</h4>
792 * The next function is the one that sets up the mesh, DoFHandler, and
793 * matrices and vectors at the beginning of the program, i.e. before the
794 * first time step. The first few lines are pretty much standard if you've
804 *
std::cout <<
"Number of active cells: " <<
triangulation.n_active_cells()
807 *
dof_handler.distribute_dofs(fe);
809 *
std::cout <<
"Number of degrees of freedom: " << dof_handler.n_dofs()
815 *
sparsity_pattern.copy_from(
dsp);
845 * @
ref threads
"Parallel computing with multiple processors"
846 *
module. The matrices for solving linear systems will be filled in the
847 * run() method because we need to re-apply boundary conditions every time
851 * mass_matrix.reinit(sparsity_pattern);
879 *
constraints.close();
887 * <a name=
"WaveEquationsolve_uandWaveEquationsolve_v"></a>
909 * void WaveEquation<dim>::solve_u()
911 * SolverControl solver_control(1000, 1e-8 * system_rhs.l2_norm());
912 * SolverCG<Vector<double>> cg(solver_control);
914 * cg.solve(matrix_u, solution_u, system_rhs, PreconditionIdentity());
916 * std::cout << " u-equation: " << solver_control.last_step()
917 * << " CG iterations." << std::endl;
923 * void WaveEquation<dim>::solve_v()
925 * SolverControl solver_control(1000, 1e-8 * system_rhs.l2_norm());
926 * SolverCG<Vector<double>> cg(solver_control);
928 * cg.solve(matrix_v, solution_v, system_rhs, PreconditionIdentity());
930 * std::cout << " v-equation: " << solver_control.last_step()
931 * << " CG iterations." << std::endl;
939 * <a name="WaveEquationoutput_results"></a>
940 * <h4>WaveEquation::output_results</h4>
944 * Likewise, the following function is pretty much what we've done
952 * void WaveEquation<dim>::output_results() const
954 * DataOut<dim> data_out;
956 * data_out.attach_dof_handler(dof_handler);
957 * data_out.add_data_vector(solution_u, "U");
958 * data_out.add_data_vector(solution_v, "V");
960 * data_out.build_patches();
962 * const std::string filename =
963 * "solution-" + Utilities::int_to_string(timestep_number, 3) + ".vtu";
966 * Like @ref step_15 "step-15", since we write output at every time step (and the system
967 * we have to solve is relatively easy), we instruct DataOut to use the
968 * zlib compression algorithm that is optimized for speed instead of disk
969 * usage since otherwise plotting the output becomes a bottleneck:
972 * DataOutBase::VtkFlags vtk_flags;
973 * vtk_flags.compression_level = DataOutBase::CompressionLevel::best_speed;
974 * data_out.set_flags(vtk_flags);
975 * std::ofstream output(filename);
976 * data_out.write_vtu(output);
984 * <a name="WaveEquationrun"></a>
985 * <h4>WaveEquation::run</h4>
989 * The following is really the only interesting function of the program. It
990 * contains the loop over all time steps, but before we get to that we have
991 * to set up the grid, DoFHandler, and matrices. In addition, we have to
992 * somehow get started with initial values. To this end, we use the
993 * VectorTools::project function that takes an object that describes a
994 * continuous function and computes the @f$L^2@f$ projection of this function
995 * onto the finite element space described by the DoFHandler object. Can't
1020 * @
f$(
M^{n,n-1} -
k^2
\theta(1-\theta) A^{n,n-1})U^{n-1} +
kM^{n,n-1}V^{n-1}
1174 *
std::cout <<
" Total energy: "
1190 * <a name=
"Thecodemaincodefunction"></a>
1196 *
that hasn't been shown in several of the previous programs:
1203 * using namespace Step23;
1205 * WaveEquation<2> wave_equation_solver;
1206 * wave_equation_solver.run();
1208 * catch (std::exception &exc)
1210 * std::cerr << std::endl
1212 * << "----------------------------------------------------"
1214 * std::cerr << "Exception on processing: " << std::endl
1215 * << exc.what() << std::endl
1216 * << "Aborting!" << std::endl
1217 * << "----------------------------------------------------"
1224 * std::cerr << std::endl
1226 * << "----------------------------------------------------"
1228 * std::cerr << "Unknown exception!" << std::endl
1229 * << "Aborting!" << std::endl
1230 * << "----------------------------------------------------"
1238<a name="Results"></a><h1>Results</h1>
1241When the program is run, it produces the following output:
1243Number of active cells: 16384
1244Number of degrees of freedom: 16641
1246Time step 1 at t=0.015625
1247 u-equation: 8 CG iterations.
1248 v-equation: 22 CG iterations.
1249 Total energy: 1.17887
1250Time step 2 at t=0.03125
1251 u-equation: 8 CG iterations.
1252 v-equation: 20 CG iterations.
1253 Total energy: 2.9655
1254Time step 3 at t=0.046875
1255 u-equation: 8 CG iterations.
1256 v-equation: 21 CG iterations.
1257 Total energy: 4.33761
1258Time step 4 at t=0.0625
1259 u-equation: 7 CG iterations.
1260 v-equation: 21 CG iterations.
1261 Total energy: 5.35499
1262Time step 5 at t=0.078125
1263 u-equation: 7 CG iterations.
1264 v-equation: 21 CG iterations.
1265 Total energy: 6.18652
1266Time step 6 at t=0.09375
1267 u-equation: 7 CG iterations.
1268 v-equation: 20 CG iterations.
1269 Total energy: 6.6799
1273Time step 31 at t=0.484375
1274 u-equation: 7 CG iterations.
1275 v-equation: 20 CG iterations.
1276 Total energy: 21.9068
1277Time step 32 at t=0.5
1278 u-equation: 7 CG iterations.
1279 v-equation: 20 CG iterations.
1280 Total energy: 23.3394
1281Time step 33 at t=0.515625
1282 u-equation: 7 CG iterations.
1283 v-equation: 20 CG iterations.
1284 Total energy: 23.1019
1288Time step 319 at t=4.98438
1289 u-equation: 7 CG iterations.
1290 v-equation: 20 CG iterations.
1291 Total energy: 23.1019
1293 u-equation: 7 CG iterations.
1294 v-equation: 20 CG iterations.
1295 Total energy: 23.1019
1298What we see immediately is that the energy is a constant at least after
1299@f$t=\frac 12@f$ (until which the boundary source term @f$g@f$ is nonzero, injecting
1300energy into the system).
1302In addition to the screen output, the program writes the solution of each time
1303step to an output file. If we process them adequately and paste them into a
1304movie, we get the following:
1306<img src="https://www.dealii.org/images/steps/developer/step-23.movie.gif" alt="Animation of the solution of step 23.">
1308The movie shows the generated wave nice traveling through the domain and back,
1309being reflected at the clamped boundary. Some numerical noise is trailing the
1310wave, an artifact of a too-large mesh size that can be reduced by reducing the
1311mesh width and the time step.
1314<a name="extensions"></a>
1315<a name="Possibilitiesforextensions"></a><h3>Possibilities for extensions</h3>
1318If you want to explore a bit, try out some of the following things:
1320 <li>Varying @f$\theta@f$. This gives different time stepping schemes, some of
1321 which are stable while others are not. Take a look at how the energy
1324 <li>Different initial and boundary conditions, right hand sides.
1326 <li>More complicated domains or more refined meshes. Remember that the time
1327 step needs to be bounded by the mesh width, so changing the mesh should
1328 always involve also changing the time step. We will come back to this issue
1329 in @ref step_24 "step-24".
1331 <li>Variable coefficients: In real media, the wave speed is often
1332 variable. In particular, the "real" wave equation in realistic media would
1335 \rho(x) \frac{\partial^2 u}{\partial t^2}
1340 where @f$\rho(x)@f$ is the density of the material, and @f$a(x)@f$ is related to the
1341 stiffness coefficient. The wave speed is then @f$c=\sqrt{a/\rho}@f$.
1343 To make such a change, we would have to compute the mass and Laplace
1344 matrices with a variable coefficient. Fortunately, this isn't
too hard:
the
1378 method. For example, the first of the two equations to be solved in each time
1379 step looked like this:
1381 (u^n,\varphi) + k^2\theta^2(\nabla u^n,\nabla \varphi) &=&
1382 (u^{n-1},\varphi) - k^2\theta(1-\theta)(\nabla u^{n-1},\nabla \varphi)
1387 \theta (f^n,\varphi) + (1-\theta) (f^{n-1},\varphi)
1390 Now, note that we solve for @f$u^n@f$ on mesh @f${\mathbb T}^n@f$, and
1391 consequently the test functions @f$\varphi@f$ have to be from the space
1392 @f$V_h^n@f$ as well. As discussed in the introduction, terms like
1393 @f$(u^{n-1},\varphi)@f$ then require us to integrate the solution of the
1394 previous step (which may have been computed on a different mesh
1395 @f${\mathbb T}^{n-1}@f$) against the test functions of the current mesh,
1396 leading to a matrix @f$M^{n,n-1}@f$. This process of integrating shape
1397 functions from different meshes is, at best, awkward. It can be done
1398 but because it is difficult to ensure that @f${\mathbb T}^{n-1}@f$ and
1399 @f${\mathbb T}^{n}@f$ differ by at most one level of refinement, one
1400 has to recursively match cells from both meshes. It is feasible to
1401 do this, but it leads to lengthy and not entirely obvious code.
1403 The second approach is the following: whenever we change the mesh,
1404 we simply interpolate the solution from the last time step on the old
1405 mesh to the new mesh, using the SolutionTransfer class. In other words,
1406 instead of the equation above, we would solve
1408 (u^n,\varphi) + k^2\theta^2(\nabla u^n,\nabla \varphi) &=&
1409 (I^n u^{n-1},\varphi) - k^2\theta(1-\theta)(\nabla I^n u^{n-1},\nabla \varphi)
1411 k(I^n v^{n-1},\varphi)
1414 \theta (f^n,\varphi) + (1-\theta) (f^{n-1},\varphi)
1417 where @f$I^n@f$ interpolates a given function onto mesh @f${\mathbb T}^n@f$.
1418 This is a much simpler approach because, in each time step, we no
1419 longer have to worry whether @f$u^{n-1},v^{n-1}@f$ were computed on the
1420 same mesh as we are using now or on a different mesh. Consequently,
1421 the only changes to the code necessary are the addition of a function
1422 that computes the error, marks cells for refinement, sets up a
1423 SolutionTransfer object, transfers the solution to the new mesh, and
1424 rebuilds matrices and right hand side vectors on the new mesh. Neither
1425 the functions building the matrices and right hand sides, nor the
1426 solvers need to be changed.
1428 While this second approach is, strictly speaking,
1429 not quite correct in the Rothe framework (it introduces an addition source
1430 of error, namely the interpolation), it is nevertheless what
1431 almost everyone solving time dependent equations does. We will use this
1432 method in @ref step_31 "step-31", for example.
1436<a name="PlainProg"></a>
1437<h1> The plain program</h1>
1438@include "step-23.cc"
value_type * data() const noexcept
void reinit(value_type *starting_element, const std::size_t n_elements)
virtual void set_time(const Number new_time)
somenumber matrix_norm_square(const Vector< somenumber > &v) const
__global__ void set(Number *val, const Number s, const size_type N)
#define Assert(cond, exc)
static ::ExceptionBase & ExcIndexRange(std::size_t arg1, std::size_t arg2, std::size_t arg3)
void loop(ITERATOR begin, std_cxx20::type_identity_t< ITERATOR > end, DOFINFO &dinfo, INFOBOX &info, const std::function< void(DOFINFO &, typename INFOBOX::CellInfo &)> &cell_worker, const std::function< void(DOFINFO &, typename INFOBOX::CellInfo &)> &boundary_worker, const std::function< void(DOFINFO &, DOFINFO &, typename INFOBOX::CellInfo &, typename INFOBOX::CellInfo &)> &face_worker, ASSEMBLER &assembler, const LoopControl &lctrl=LoopControl())
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=AffineConstraints< number >(), const bool keep_constrained_dofs=true, const types::subdomain_id subdomain_id=numbers::invalid_subdomain_id)
void apply(const Kokkos::TeamPolicy< MemorySpace::Default::kokkos_space::execution_space >::member_type &team_member, const Kokkos::View< Number *, MemorySpace::Default::kokkos_space > shape_data, const ViewTypeIn in, ViewTypeOut out)
void hyper_cube(Triangulation< dim, spacedim > &tria, const double left=0., const double right=1., const bool colorize=false)
@ matrix
Contents is actually a matrix.
@ general
No special properties.
void mass_matrix(FullMatrix< double > &M, const FEValuesBase< dim > &fe, const double factor=1.)
void create_mass_matrix(const Mapping< dim, spacedim > &mapping, const DoFHandler< dim, spacedim > &dof, const Quadrature< dim > &q, SparseMatrixType &matrix, const Function< spacedim, typename SparseMatrixType::value_type > *const a=nullptr, const AffineConstraints< typename SparseMatrixType::value_type > &constraints=AffineConstraints< typename SparseMatrixType::value_type >())
void create_laplace_matrix(const Mapping< dim, spacedim > &mapping, const DoFHandler< dim, spacedim > &dof, const Quadrature< dim > &q, SparseMatrixType &matrix, const Function< spacedim, typename SparseMatrixType::value_type > *const a=nullptr, const AffineConstraints< typename SparseMatrixType::value_type > &constraints=AffineConstraints< typename SparseMatrixType::value_type >())
Point< spacedim > point(const gp_Pnt &p, const double tolerance=1e-10)
SymmetricTensor< 2, dim, Number > e(const Tensor< 2, dim, Number > &F)
Tensor< 2, dim, Number > F(const Tensor< 2, dim, Number > &Grad_u)
void call(const std::function< RT()> &function, internal::return_value< RT > &ret_val)
VectorType::value_type * end(VectorType &V)
std::string int_to_string(const unsigned int value, const unsigned int digits=numbers::invalid_unsigned_int)
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)
int(&) functions(const void *v1, const void *v2)
static constexpr double PI
::VectorizedArray< Number, width > cos(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > sin(const ::VectorizedArray< Number, width > &)
const ::parallel::distributed::Triangulation< dim, spacedim > * triangulation
DEAL_II_HOST constexpr SymmetricTensor< 2, dim, Number > invert(const SymmetricTensor< 2, dim, Number > &)